diff --git a/drivers/misc/lkdtm/Makefile b/drivers/misc/lkdtm/Makefile index 0d768a13e2bb705ff645df6cf1e3d1c7a0055726..db60ec84d94b8fefd66dfaf277a001037ea6cbb6 100644 --- a/drivers/misc/lkdtm/Makefile +++ b/drivers/misc/lkdtm/Makefile @@ -10,6 +10,7 @@ lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o lkdtm-$(CONFIG_LKDTM) += usercopy.o lkdtm-$(CONFIG_LKDTM) += stackleak.o lkdtm-$(CONFIG_LKDTM) += cfi.o +lkdtm-$(CONFIG_LKDTM) += xpm.o KASAN_SANITIZE_stackleak.o := n KCOV_INSTRUMENT_rodata.o := n diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c index 32b3d77368e37d734ad541e1cda80997fa733345..d00a57865166e4f6d0a67ee20a2b055ab34d31c0 100644 --- a/drivers/misc/lkdtm/core.c +++ b/drivers/misc/lkdtm/core.c @@ -174,6 +174,10 @@ static const struct crashtype crashtypes[] = { CRASHTYPE(STACKLEAK_ERASING), CRASHTYPE(CFI_FORWARD_PROTO), CRASHTYPE(DOUBLE_FAULT), + CRASHTYPE(XPM_ELF_CODE_SEGMENT), + CRASHTYPE(XPM_ELF_CODE_SEGMENT_ABC), + CRASHTYPE(XPM_ELF_CODE_SEGMENT_SIZE), + CRASHTYPE(XPM_ELF_CODE_SEGMENT_FULL), }; diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h index 6dec4c9b442ff34e9c516e54ee8d4706af7ad0bd..ddc566f4d1f369b08f57a67c209839694c861cd8 100644 --- a/drivers/misc/lkdtm/lkdtm.h +++ b/drivers/misc/lkdtm/lkdtm.h @@ -102,4 +102,8 @@ void lkdtm_STACKLEAK_ERASING(void); /* cfi.c */ void lkdtm_CFI_FORWARD_PROTO(void); +void lkdtm_XPM_ELF_CODE_SEGMENT(void); +void lkdtm_XPM_ELF_CODE_SEGMENT_ABC(void); +void lkdtm_XPM_ELF_CODE_SEGMENT_SIZE(void); +void lkdtm_XPM_ELF_CODE_SEGMENT_FULL(void); #endif diff --git a/drivers/misc/lkdtm/xpm.c b/drivers/misc/lkdtm/xpm.c new file mode 100644 index 0000000000000000000000000000000000000000..17767b02b007f8d601e76701e07f79518b2e2666 --- /dev/null +++ b/drivers/misc/lkdtm/xpm.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This is for all the tests relating directly to heap memory, including + * page allocation and slab allocations. + */ +#include "lkdtm.h" +#include +#include +#include +#include +#include "../fs/mount.h" + +struct exec_file_signature_info; +int get_exec_file_signature_info(struct file*, bool, struct exec_file_signature_info **); +int put_exec_file_signature_info(struct exec_file_signature_info *); +int test_destroy_elf_code_segment_info_cache(void); +void test_print_elf_code_segment_info(const char *file_path, const struct exec_file_signature_info *file_info); +int test_delete_elf_code_segment_info(struct exec_file_signature_info *code_segment_info); +size_t test_get_elf_code_segment_info_cache_size(int *cache_count); + +/* + * This tries to stay within the next largest power-of-2 kmalloc cache + * to avoid actually overwriting anything important if it's not detected + * correctly. + */ +void lkdtm_XPM_ELF_CODE_SEGMENT(void) +{ + int ret; + struct exec_file_signature_info *file_info = NULL; + struct exec_file_signature_info *file_info_cache = NULL; + char file_path[PATH_MAX] = "/system/bin/dmesg"; + struct file *test_file = filp_open(file_path, O_RDONLY, 0); + if (test_file == NULL) { + pr_info("[%s:%d] filp_open failed\n", __FUNCTION__, __LINE__); + return; + } + + ret = get_exec_file_signature_info(test_file, true, &file_info); + if (ret < 0) { + filp_close(test_file, 0); + pr_info("[%s:%d] get_exec_file_signature_info failed\n", __FUNCTION__, __LINE__); + return; + } + + ret = put_exec_file_signature_info(file_info); + if (ret < 0) { + filp_close(test_file, 0); + pr_info("[%s:%d] put_exec_file_signature_info failed\n", __FUNCTION__, __LINE__); + return; + } + + (void)get_exec_file_signature_info(test_file, true, &file_info_cache); + (void)put_exec_file_signature_info(file_info_cache); + + if (file_info_cache != file_info) { + pr_info("[%s:%d] get cache failed!\n", __FUNCTION__, __LINE__); + } + + test_print_elf_code_segment_info(file_path, file_info); + + ret = test_delete_elf_code_segment_info(file_info); + if (ret < 0) { + filp_close(test_file, 0); + pr_info("[%s:%d] delete_elf_code_segment_info failed\n", __FUNCTION__, __LINE__); + return; + } + filp_close(test_file, 0); +} + +void lkdtm_XPM_ELF_CODE_SEGMENT_ABC(void) +{ + int ret; + struct exec_file_signature_info *file_info = NULL; + struct exec_file_signature_info *file_info_cache = NULL; + char file_path[PATH_MAX] = "/system/bin/dmesg"; + struct file *test_file = filp_open(file_path, O_RDONLY, 0); + if (test_file == NULL) { + pr_info("[%s:%d] filp_open failed\n", __FUNCTION__, __LINE__); + return; + } + + ret = get_exec_file_signature_info(test_file, false, &file_info); + if (ret < 0) { + filp_close(test_file, 0); + pr_info("[%s:%d] get_exec_file_signature_info failed\n", __FUNCTION__, __LINE__); + return; + } + + ret = put_exec_file_signature_info(file_info); + if (ret < 0) { + filp_close(test_file, 0); + pr_info("[%s:%d] put_exec_file_signature_info failed\n", __FUNCTION__, __LINE__); + return; + } + + (void)get_exec_file_signature_info(test_file, false, &file_info_cache); + (void)put_exec_file_signature_info(file_info_cache); + + if (file_info_cache != file_info) { + pr_info("[%s:%d] get cache failed!\n", __FUNCTION__, __LINE__); + } + + ret = test_delete_elf_code_segment_info(file_info); + if (ret < 0) { + filp_close(test_file, 0); + pr_info("[%s:%d] delete_elf_code_segment_info failed\n", __FUNCTION__, __LINE__); + return; + } + test_print_elf_code_segment_info(file_path, file_info); + filp_close(test_file, 0); +} + +void lkdtm_XPM_ELF_CODE_SEGMENT_FULL(void) +{ + char file_path[PATH_MAX] = "/system/bin/dmesg"; + struct file *test_file = filp_open(file_path, O_RDONLY, 0); + if (test_file == NULL) { + pr_info("[%s:%d] filp_open failed\n", __FUNCTION__, __LINE__); + return; + } + + struct mount *mnt = container_of(test_file->f_path.mnt, struct mount, mnt); + struct vfsmount *vfsmnt = test_file->f_path.mnt; + pr_info("test_file mount root: %s mountpointer: %s -> dev name: %s\n", vfsmnt->mnt_root->d_iname, mnt->mnt_mountpoint->d_iname, mnt->mnt_devname); + filp_close(test_file, 0); +} + +void lkdtm_XPM_ELF_CODE_SEGMENT_SIZE(void) +{ + int count; + size_t cache_size = test_get_elf_code_segment_info_cache_size(&count); + pr_info("cache count: %d cache size: %lu KB\n", count, cache_size / 1024); +} diff --git a/mm/mmap.c b/mm/mmap.c index bccc3235cd61f37f2d30dce0f1a7e1828a8dfdb3..3d16e4f7ceae737410840a7bfb198aafdd5c0370 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1408,6 +1408,9 @@ static inline bool file_mmap_ok(struct file *file, struct inode *inode, return true; } +struct exec_file_signature_info; +int get_exec_file_signature_info(struct file*, bool, struct exec_file_signature_info **); +int put_exec_file_signature_info(struct exec_file_signature_info *); /* * The caller must write-lock current->mm->mmap_lock. */ @@ -1420,7 +1423,7 @@ unsigned long do_mmap(struct file *file, unsigned long addr, vm_flags_t vm_flags; int pkey = 0; int err = 0; - + struct exec_file_signature_info *sig_info; *populate = 0; if (!len) @@ -1495,6 +1498,14 @@ unsigned long do_mmap(struct file *file, unsigned long addr, return -EAGAIN; if (file) { + if (prot & PROT_EXEC) { + int ret = get_exec_file_signature_info(file, true, &sig_info); + if (ret == 0) { + put_exec_file_signature_info(sig_info); + } else { + printk("[%s: %d] get_exec_file_signature_info failed, error=%d\n", __FUNCTION__, __LINE__, ret); + } + } struct inode *inode = file_inode(file); unsigned long flags_mask; diff --git a/security/Makefile b/security/Makefile index 3baf435de5411b2d2f5965a75faf327b4b79355b..3c88307f51868f46aa62693c920fe4688e08884e 100644 --- a/security/Makefile +++ b/security/Makefile @@ -36,3 +36,5 @@ obj-$(CONFIG_BPF_LSM) += bpf/ # Object integrity file lists subdir-$(CONFIG_INTEGRITY) += integrity obj-$(CONFIG_INTEGRITY) += integrity/ +obj-y += xpm/validator/elf_code_segment_info.o +obj-y += xpm/validator/exec_signature_info.o diff --git a/security/xpm/validator/elf_code_segment_info.c b/security/xpm/validator/elf_code_segment_info.c new file mode 100644 index 0000000000000000000000000000000000000000..4a3969553c30ee311becaba8ae310f8992b787d0 --- /dev/null +++ b/security/xpm/validator/elf_code_segment_info.c @@ -0,0 +1,582 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ +#include +#include +#include +#include +#include +#include + +#include "exec_signature_info.h" + +struct elf_segment_info { + struct elfhdr elf_ehdr; + uint16_t type; + uint16_t e_phnum; + size_t e_phsize; + uintptr_t e_phoff; +}; + +#define VERITY_NODE_CACHE_LIMITS 10000 +#define VERITY_NODE_CACHE_RECYCLE_NUM 200 + +static DEFINE_RWLOCK(dm_verity_tree_lock); +static struct rb_root dm_verity_tree = RB_ROOT; +static int dm_verity_node_count = 0; +static DEFINE_RWLOCK(fs_verity_tree_lock); +static struct rb_root fs_verity_tree = RB_ROOT; +static int fs_verity_node_count = 0; + +static struct exec_file_signature_info *rb_search_node(struct rb_root *root, struct inode *file_inode) +{ + struct rb_node *node = root->rb_node; + struct exec_file_signature_info *file_node; + + while (node != NULL) { + file_node = rb_entry(node, struct exec_file_signature_info, 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 { + atomic_inc(&file_node->reference); + return file_node; + } + } + return NULL; +} + +static void rb_add_node(struct rb_root *root, int *node_count, struct exec_file_signature_info *node) +{ + struct rb_node **p = &root->rb_node; + struct rb_node *parent = NULL; + struct exec_file_signature_info *file; + + while (*p != NULL) { + parent = *p; + file = rb_entry(parent, struct exec_file_signature_info, 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); + atomic_inc(&node->reference); + (*node_count)++; +} + +static void rb_erase_node(struct rb_root *root, int *node_count, struct exec_file_signature_info *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 -EIO; + } + return 0; +} + +static int find_idle_nodes(struct rb_root *root, uintptr_t *ilde_nodes, size_t count) +{ + int i = 0; + struct exec_file_signature_info *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 exec_file_signature_info, rb_node); + if (atomic_read(&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 = VERITY_NODE_CACHE_RECYCLE_NUM; + 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 exec_file_signature_info *code_segment_info = (struct exec_file_signature_info *)code_segments[i]; + rb_erase_node(root, node_count, code_segment_info); + kfree(code_segment_info); + i++; + } + kfree(code_segments); +} + +static void rm_code_segment_info(void) +{ + if ((dm_verity_node_count + fs_verity_node_count) < VERITY_NODE_CACHE_LIMITS) { + return; + } + + if (dm_verity_node_count > fs_verity_node_count) { + write_lock(&dm_verity_tree_lock); + clear_code_segment_info_cache(&dm_verity_tree, &dm_verity_node_count); + write_unlock(&dm_verity_tree_lock); + return; + } + + write_lock(&fs_verity_tree_lock); + clear_code_segment_info_cache(&fs_verity_tree, &fs_verity_node_count); + write_unlock(&fs_verity_tree_lock); +} + +static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value) +{ + if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) + value = le64_to_cpu(value); + else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) + value = be64_to_cpu(value); + + return value; +} + +static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value) +{ + if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) + value = le32_to_cpu(value); + else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) + value = be32_to_cpu(value); + + return value; +} + +static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value) +{ + if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) + value = le16_to_cpu(value); + else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB) + value = be16_to_cpu(value); + + return value; +} + +static int get_elf32_code_segment_count(struct elf32_phdr *elf_phdr, struct elf_segment_info *segment_info) +{ + int i; + int count = 0; + struct elf32_phdr *phdr_info; + uint32_t p_flags; + + for (i = 0; i < segment_info->e_phnum; i++) { + phdr_info = elf_phdr + i; + p_flags = elf32_to_cpu(&segment_info->elf_ehdr, phdr_info->p_flags); + if (!(p_flags & PF_X)) { + continue; + } + count++; + } + return count; +} + +static int get_elf32_code_segment(struct elf32_phdr *elf_phdr, struct elf_segment_info *segment_info, struct exec_file_signature_info *exec_file_info) +{ + int i; + struct elf32_phdr *phdr_info; + uint32_t p_flags; + uint32_t p_offset; + uint32_t p_filesz; + uint32_t p_memsz; + uint32_t p_addr; + + for (i = 0; i < segment_info->e_phnum; i++) { + phdr_info = elf_phdr + i; + p_flags = elf32_to_cpu(&segment_info->elf_ehdr, phdr_info->p_flags); + if (!(p_flags & PF_X)) { + continue; + } + p_offset = elf32_to_cpu(&segment_info->elf_ehdr, phdr_info->p_offset); + p_filesz = elf32_to_cpu(&segment_info->elf_ehdr, phdr_info->p_filesz); + p_addr = elf32_to_cpu(&segment_info->elf_ehdr, phdr_info->p_paddr); + p_memsz = elf32_to_cpu(&segment_info->elf_ehdr, phdr_info->p_memsz); + if (p_offset + p_filesz < p_offset || p_addr + p_memsz < p_addr) { + return -ENOEXEC; + } + exec_file_info->code_segment[exec_file_info->code_section_count].file_offset = p_offset; + exec_file_info->code_segment[exec_file_info->code_section_count].size = p_filesz; + exec_file_info->code_section_count++; + } + return 0; +} + +static int get_elf64_code_segment_count(struct elf64_phdr *elf_phdr, struct elf_segment_info *segment_info) +{ + int i; + int count = 0; + struct elf64_phdr *phdr_info; + uint32_t p_flags; + + for (i = 0; i < segment_info->e_phnum; i++) { + phdr_info = elf_phdr + i; + p_flags = elf32_to_cpu(&segment_info->elf_ehdr, phdr_info->p_flags); + if (!(p_flags & PF_X)) { + continue; + } + count++; + } + return count; +} + +static int get_elf64_code_segment(struct elf64_phdr *elf_phdr, struct elf_segment_info *segment_info, struct exec_file_signature_info *exec_file_info) +{ + int i; + struct elf64_phdr *phdr_info; + uint32_t p_flags; + uint64_t p_offset; + uint64_t p_filesz; + uint64_t p_memsz; + uint64_t p_addr; + + for (i = 0; i < segment_info->e_phnum; i++) { + phdr_info = elf_phdr + i; + p_flags = elf32_to_cpu(&segment_info->elf_ehdr, phdr_info->p_flags); + if (!(p_flags & PF_X)) { + continue; + } + p_offset = elf64_to_cpu(&segment_info->elf_ehdr, phdr_info->p_offset); + p_filesz = elf64_to_cpu(&segment_info->elf_ehdr, phdr_info->p_filesz); + p_addr = elf64_to_cpu(&segment_info->elf_ehdr, phdr_info->p_paddr); + p_memsz = elf64_to_cpu(&segment_info->elf_ehdr, phdr_info->p_memsz); + if (p_offset + p_filesz < p_offset || p_addr + p_memsz < p_addr) { + return -ENOEXEC; + } + exec_file_info->code_segment[exec_file_info->code_section_count].file_offset = p_offset; + exec_file_info->code_segment[exec_file_info->code_section_count].size = p_filesz; + exec_file_info->code_section_count++; + } + return 0; +} + +static int elf_check_and_get_code_segment_offset(struct file *file, struct elf_segment_info *segment_info) +{ + struct elf32_hdr *elf32_ehdr; + struct elf64_hdr *elf64_ehdr; + uint32_t e32_phoff; + uint32_t e32_phsize; + uint64_t e64_phoff; + uint64_t e64_phsize; + uint16_t type; + uint16_t e_ehsize; + + struct elfhdr *elf_ehdr = &segment_info->elf_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; + } + + type = elf16_to_cpu(elf_ehdr, elf_ehdr->e_type); + if (type != ET_EXEC && type != ET_DYN) { + return -ENOEXEC; + } + + if (elf_ehdr->e_ident[EI_CLASS] == ELFCLASS32) { + segment_info->type = ELFCLASS32; + elf32_ehdr = (struct elf32_hdr *)elf_ehdr; + e_ehsize = elf16_to_cpu(elf_ehdr, elf32_ehdr->e_ehsize); + if (e_ehsize != sizeof(struct elf32_hdr)) { + return -ENOEXEC; + } + segment_info->e_phnum = elf16_to_cpu(elf_ehdr, elf32_ehdr->e_phnum); + if (segment_info->e_phnum == 0) { + return -ENOEXEC; + } + e32_phsize = sizeof(struct elf32_phdr) * segment_info->e_phnum; + e32_phoff = elf32_to_cpu(elf_ehdr, elf32_ehdr->e_phoff); + if (e32_phoff > 0 && e32_phoff + e32_phsize < e32_phoff) { + return -ENOEXEC; + } + segment_info->e_phsize = e32_phsize; + segment_info->e_phoff = e32_phoff; + } else if (elf_ehdr->e_ident[EI_CLASS] == ELFCLASS64) { + segment_info->type = ELFCLASS64; + elf64_ehdr = (struct elf64_hdr *)elf_ehdr; + e_ehsize = elf16_to_cpu(elf_ehdr, elf64_ehdr->e_ehsize); + if (e_ehsize != sizeof(struct elf64_hdr)) { + return -ENOEXEC; + } + segment_info->e_phnum = elf16_to_cpu(elf_ehdr, elf64_ehdr->e_phnum); + if (segment_info->e_phnum == 0) { + return -ENOEXEC; + } + e64_phsize = sizeof(struct elf64_phdr) * segment_info->e_phnum; + e64_phoff = elf64_to_cpu(elf_ehdr, elf64_ehdr->e_phoff); + if (e64_phoff > 0 && e64_phoff + e64_phsize < e64_phoff) { + return -ENOEXEC; + } + segment_info->e_phsize = e64_phsize; + segment_info->e_phoff = e64_phoff; + } else { + return -ENOEXEC; + } + return 0; +} + +static int find_elf_code_segment_info(const char *phdr_info, struct elf_segment_info *segment_info, struct exec_file_signature_info **file_info) +{ + int ret; + size_t size; + struct exec_file_signature_info *exec_file_info; + int segment_count; + + if (segment_info->type == ELFCLASS32) { + segment_count = get_elf32_code_segment_count((struct elf32_phdr *)phdr_info, segment_info); + } else { + segment_count = get_elf64_code_segment_count((struct elf64_phdr *)phdr_info, segment_info); + } + if (segment_count == 0) { + return -ENOEXEC; + } + + size = sizeof(struct exec_file_signature_info) + segment_count * sizeof(struct exec_segment_info); + exec_file_info = (struct exec_file_signature_info *)kzalloc(size, GFP_KERNEL); + if (exec_file_info == NULL) { + return -ENOMEM; + } + exec_file_info->code_segment = (struct exec_segment_info *)((char *)exec_file_info + sizeof(struct exec_file_signature_info)); + if (segment_info->type == ELFCLASS32) { + ret = get_elf32_code_segment((struct elf32_phdr *)phdr_info, segment_info, exec_file_info); + } else { + ret = get_elf64_code_segment((struct elf64_phdr *)phdr_info, segment_info, exec_file_info); + } + if (ret < 0) { + kfree(exec_file_info); + return ret; + } + *file_info = exec_file_info; + return 0; +} + +static int parse_elf_code_segment_info(struct file *file, struct exec_file_signature_info **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, bool is_exec, int type, struct exec_file_signature_info **code_segment_info) +{ + int ret; + struct rb_root *root; + rwlock_t *verity_lock; + int *node_count; + struct inode *file_node; + struct exec_file_signature_info *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; + } + + read_lock(verity_lock); + segment_info = rb_search_node(root, file_node); + read_unlock(verity_lock); + if (segment_info != NULL) { + *code_segment_info = segment_info; + return 0; + } + + rm_code_segment_info(); + + if (!is_exec) { + segment_info = (struct exec_file_signature_info *)kzalloc(sizeof(struct exec_file_signature_info), GFP_KERNEL); + if (segment_info == NULL) { + return -ENOMEM; + } + } else { + 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); + + write_lock(verity_lock); + rb_add_node(root, node_count, segment_info); + write_unlock(verity_lock); + *code_segment_info = segment_info; + return 0; +} + +int put_elf_code_segment_info(struct exec_file_signature_info *code_segment_info) +{ + if ((code_segment_info == NULL) || + ((code_segment_info->type != FILE_DM_VERITY) && (code_segment_info->type != FILE_FS_VERITY))) { + return -EINVAL; + } + + atomic_dec_and_test(&code_segment_info->reference); + return 0; +} + +int test_delete_elf_code_segment_info(struct exec_file_signature_info *code_segment_info) +{ + struct rb_root *root; + rwlock_t *verity_lock; + int *node_count; + struct exec_file_signature_info *segment_info = NULL; + + if (code_segment_info == NULL) { + return -EINVAL; + } + + if (code_segment_info->type == FILE_DM_VERITY) { + root = &dm_verity_tree; + verity_lock = &dm_verity_tree_lock; + node_count = &dm_verity_node_count; + } else if (code_segment_info->type == FILE_FS_VERITY) { + verity_lock = &fs_verity_tree_lock; + root = &fs_verity_tree; + node_count = &fs_verity_node_count; + } else { + return -EINVAL; + } + + write_lock(verity_lock); + segment_info = rb_search_node(root, code_segment_info->inode); + if (segment_info == NULL) { + write_unlock(verity_lock); + return -EINVAL; + } + rb_erase_node(root, node_count, code_segment_info); + write_unlock(verity_lock); + kfree(code_segment_info); + return 0; +} + +static int destroy_elf_code_segment_tree(struct rb_root *root, int *node_count) +{ + struct rb_node *node; + struct exec_file_signature_info *file_node; + + do { + node = rb_first(root); + if (node == NULL) { + return 0; + } + file_node = rb_entry(node, struct exec_file_signature_info, rb_node); + if (atomic_read(&file_node->reference) > 0) { + return -EPERM; + } + rb_erase_node(root, node_count, file_node); + } while (1); + return 0; +} + +int test_destroy_elf_code_segment_info_cache(void) +{ + int ret; + int count = 0; + + write_lock(&dm_verity_tree_lock); + count += dm_verity_node_count; + ret = destroy_elf_code_segment_tree(&dm_verity_tree, &dm_verity_node_count); + write_unlock(&dm_verity_tree_lock); + if (ret < 0) { + return ret; + } + + write_lock(&fs_verity_tree_lock); + count += fs_verity_node_count; + ret = destroy_elf_code_segment_tree(&fs_verity_tree, &fs_verity_node_count); + write_unlock(&fs_verity_tree_lock); + if (ret < 0) { + return ret; + } + return count; +} + +static size_t elf_code_segment_info_size(struct rb_root *root) +{ + size_t size = 0; + struct exec_file_signature_info *file_node; + struct rb_node *node; + for (node = rb_first(root); node != NULL; node = rb_next(node)) { + file_node = rb_entry(node, struct exec_file_signature_info, rb_node); + size += sizeof(struct exec_file_signature_info) + file_node->code_section_count * sizeof(struct exec_segment_info); + } + return size; +} + +size_t test_get_elf_code_segment_info_cache_size(int *cache_count) +{ + size_t cache_size = 0; + int count = 0; + + read_lock(&dm_verity_tree_lock); + cache_size += elf_code_segment_info_size(&dm_verity_tree); + count += dm_verity_node_count; + read_unlock(&dm_verity_tree_lock); + + read_lock(&fs_verity_tree_lock); + cache_size += elf_code_segment_info_size(&fs_verity_tree); + count += fs_verity_node_count; + read_unlock(&fs_verity_tree_lock); + + if (cache_count) { + *cache_count = count; + } + return cache_size; +} + +void test_print_elf_code_segment_info(const char *file_path, const struct exec_file_signature_info *file_info) +{ + int i; + for (i = 0; i < file_info->code_section_count; i++) { + pr_info("%s -> offset: 0x%llx size: 0x%lx\n", file_path, file_info->code_segment->file_offset, file_info->code_segment->size); + } +} diff --git a/security/xpm/validator/exec_signature_info.c b/security/xpm/validator/exec_signature_info.c new file mode 100644 index 0000000000000000000000000000000000000000..0d84920d4c8aee376160433dc4969b12e0ff6169 --- /dev/null +++ b/security/xpm/validator/exec_signature_info.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ +#include +#include +#include +#include +#include "exec_signature_info.h" + +static int check_exec_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; + } + +#ifdef CONFIG_FS_VERITY + if (file_node->i_verity_info != NULL) { + return FILE_FS_VERITY; + } +#endif + return FILE_DM_VERITY; +} + +int get_exec_file_signature_info(struct file *file, bool is_exec, struct exec_file_signature_info **info_ptr) +{ + int type; + + if (file == NULL || info_ptr == NULL) { + return -EINVAL; + } + + struct inode *file_node = file_inode(file); + if (file_node == NULL) { + return -EINVAL; + } + + type = check_exec_file_is_verity(file, file_node); + return get_elf_code_segment_info(file, is_exec, type, info_ptr); +} + +int put_exec_file_signature_info(struct exec_file_signature_info *exec_info) +{ + return put_elf_code_segment_info(exec_info); +} diff --git a/security/xpm/validator/exec_signature_info.h b/security/xpm/validator/exec_signature_info.h new file mode 100644 index 0000000000000000000000000000000000000000..0d3ec225cc03d666e873bd3d2323c6a673890627 --- /dev/null +++ b/security/xpm/validator/exec_signature_info.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ +#ifndef _EXEC_SIGNATURE_INFO_H +#define _EXEC_SIGNATURE_INFO_H + +#include +#include +#include + +struct exec_segment_info { + unsigned long file_offset; + unsigned long size; +}; + +#define FILE_FS_VERITY 0 +#define FILE_DM_VERITY 1 +#define FILE_NORMAL 2 + +struct exec_file_signature_info { + struct rb_node rb_node; + atomic_t reference; + int type; + struct inode *inode; + uint8_t hash[32]; + int code_section_count; + struct exec_segment_info *code_segment; +}; + +void test_print_elf_code_segment_info(const char *file_path, const struct exec_file_signature_info *file_info); +int test_destroy_elf_code_segment_info_cache(void); +int test_delete_elf_code_segment_info(struct exec_file_signature_info *code_segment_info); +int put_elf_code_segment_info(struct exec_file_signature_info *code_segment_info); +int get_elf_code_segment_info(struct file *file, bool is_exec, int type, struct exec_file_signature_info **code_segment_info); +int get_exec_file_signature_info(struct file *file, bool is_exec, struct exec_file_signature_info **info_ptr); +int put_exec_file_signature_info(struct exec_file_signature_info *exec_info); +#endif