diff --git a/drivers/hck/vendor_hooks.c b/drivers/hck/vendor_hooks.c index d3e3e2befd9f085f44c530e0933d763bb1615a7e..4fd1a37c61e7a375d4d9b3c62da4f4a6f2eb6b05 100644 --- a/drivers/hck/vendor_hooks.c +++ b/drivers/hck/vendor_hooks.c @@ -9,3 +9,4 @@ #define CREATE_LITE_VENDOR_HOOK /* add your lite vendor hook header file here */ #include +#include diff --git a/fs/inode.c b/fs/inode.c index 9f49e0bdc2f77bee7207050071577d31e7cdeeab..e56bbfbb0c34145c31ecbba11f38c2d7de30a08c 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "internal.h" @@ -258,6 +259,7 @@ void __destroy_inode(struct inode *inode) security_inode_free(inode); fsnotify_inode_delete(inode); locks_free_lock_context(inode); + xpm_delete_cache_node_hook(inode); if (!inode->i_nlink) { WARN_ON(atomic_long_read(&inode->i_sb->s_remove_count) == 0); atomic_long_dec(&inode->i_sb->s_remove_count); diff --git a/fs/proc/Makefile b/fs/proc/Makefile index bd08616ed8bad7937173183eb08634c9526a4e90..bcbca3ed17c9f261c504154d16fee66f71538bd3 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile @@ -34,3 +34,4 @@ proc-$(CONFIG_PROC_VMCORE) += vmcore.o proc-$(CONFIG_PRINTK) += kmsg.o proc-$(CONFIG_PROC_PAGE_MONITOR) += page.o proc-$(CONFIG_BOOT_CONFIG) += bootconfig.o +proc-$(CONFIG_SECURITY_XPM) += xpm_region.o diff --git a/fs/proc/base.c b/fs/proc/base.c index 27145778c144f26473ca8b8ac9d703b9173abe74..9c86e723f65d9a4873182138d42d5c7e627c605f 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -3459,6 +3459,9 @@ static const struct pid_entry tgid_base_stuff[] = { #ifdef CONFIG_SCHED_RTG_DEBUG REG("sched_group_id", S_IRUGO|S_IWUGO, proc_pid_sched_group_id_operations), #endif +#ifdef CONFIG_SECURITY_XPM + REG("xpm_region", S_IRUSR|S_IRGRP, proc_xpm_region_operations), +#endif }; static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx) @@ -3794,6 +3797,9 @@ static const struct pid_entry tid_base_stuff[] = { #ifdef CONFIG_SCHED_RTG_DEBUG REG("sched_group_id", S_IRUGO|S_IWUGO, proc_pid_sched_group_id_operations), #endif +#ifdef CONFIG_SECURITY_XPM + REG("xpm_region", S_IRUSR|S_IRGRP, proc_xpm_region_operations), +#endif }; static int proc_tid_base_readdir(struct file *file, struct dir_context *ctx) diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 8b91a8395e1dbf0cec529ba80c303e738b9c5522..1c002424f5b59fd983a4c285ce36feea3af4e527 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -310,6 +310,7 @@ extern const struct file_operations proc_pid_smaps_operations; extern const struct file_operations proc_pid_smaps_rollup_operations; extern const struct file_operations proc_clear_refs_operations; extern const struct file_operations proc_pagemap_operations; +extern const struct file_operations proc_xpm_region_operations; extern unsigned long task_vsize(struct mm_struct *); extern unsigned long task_statm(struct mm_struct *, diff --git a/fs/proc/xpm_region.c b/fs/proc/xpm_region.c new file mode 100644 index 0000000000000000000000000000000000000000..c2eab93e3ba2878038c3272a18762d8f9e363159 --- /dev/null +++ b/fs/proc/xpm_region.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + */ + +#include +#include +#include "internal.h" + +#define XPM_REGION_LEN 48 +static int xpm_region_open(struct inode *inode, struct file *file) +{ + struct mm_struct *mm = proc_mem_open(inode, PTRACE_MODE_READ); + + if (IS_ERR(mm)) + return PTR_ERR(mm); + + file->private_data = mm; + return 0; +} + +static ssize_t xpm_region_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct mm_struct *mm = file->private_data; + char xpm_region[XPM_REGION_LEN] = {0}; + size_t len; + + if (!mm) + return 0; + + len = snprintf(xpm_region, XPM_REGION_LEN - 1, "%lx-%lx", + mm->xpm_region.addr_start, + mm->xpm_region.addr_end); + + return simple_read_from_buffer(buf, count, pos, xpm_region, len); +} + +static int xpm_region_release(struct inode *inode, struct file *file) +{ + struct mm_struct *mm = file->private_data; + + if (mm) + mmdrop(mm); + + return 0; +} + +const struct file_operations proc_xpm_region_operations = { + .open = xpm_region_open, + .read = xpm_region_read, + .llseek = generic_file_llseek, + .release = xpm_region_release, +}; \ No newline at end of file diff --git a/include/linux/hck/lite_hck_xpm.h b/include/linux/hck/lite_hck_xpm.h new file mode 100644 index 0000000000000000000000000000000000000000..0ec0063d34004ca750b102c48ad9e2544060a245 --- /dev/null +++ b/include/linux/hck/lite_hck_xpm.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ + +#ifndef _LITE_HCK_XPM_H +#define _LITE_HCK_XPM_H + +#include +#include +#include + +#ifndef CONFIG_HCK +#undef CALL_HCK_LITE_HOOK +#define CALL_HCK_LITE_HOOK(name, args...) +#undef REGISTER_HCK_LITE_HOOK +#define REGISTER_HCK_LITE_HOOK(name, probe) +#undef REGISTER_HCK_LITE_DATA_HOOK +#define REGISTER_HCK_LITE_DATA_HOOK(name, probe, data) +#else +DECLARE_HCK_LITE_HOOK(xpm_delete_cache_node_lhck, + TP_PROTO(struct inode *file_node), + TP_ARGS(file_node)); + +DECLARE_HCK_LITE_HOOK(xpm_region_outer_lhck, + TP_PROTO(unsigned long addr_start, unsigned long addr_end, + unsigned long flags, bool *ret), + TP_ARGS(addr_start, addr_end, flags, ret)); + +DECLARE_HCK_LITE_HOOK(xpm_get_unmapped_area_lhck, + TP_PROTO(unsigned long addr, unsigned long len, unsigned long map_flags, + unsigned long unmapped_flags, unsigned long *ret), + TP_ARGS(addr, len, map_flags, unmapped_flags, ret)); + +DECLARE_HCK_LITE_HOOK(xpm_integrity_equal_lhck, + TP_PROTO(struct page *page, struct page *kpage, bool *ret), + TP_ARGS(page, kpage, ret)); + +DECLARE_HCK_LITE_HOOK(xpm_integrity_check_lhck, + TP_PROTO(struct vm_area_struct *vma, unsigned int vflags, + unsigned long addr, struct page *page, vm_fault_t *ret), + TP_ARGS(vma, vflags, addr, page, ret)); + +DECLARE_HCK_LITE_HOOK(xpm_integrity_validate_lhck, + TP_PROTO(struct vm_area_struct *vma, unsigned int vflags, + unsigned long addr, struct page *page, vm_fault_t *ret), + TP_ARGS(vma, vflags, addr, page, ret)); + +DECLARE_HCK_LITE_HOOK(xpm_integrity_update_lhck, + TP_PROTO(struct vm_area_struct *vma, unsigned int vflags, + struct page *page), + TP_ARGS(vma, vflags, page)); +#endif /* CONFIG_HCK */ + +#endif /* _LITE_HCK_XPM_H */ diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index d13631a5e908760eb5b07e065bda9fa14256d892..d7c61d382c4afca33d4327dd69cd695d7a26356b 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -161,6 +161,9 @@ LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd, LSM_HOOK(int, 0, mmap_addr, unsigned long addr) LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot, unsigned long prot, unsigned long flags) +#ifdef CONFIG_SECURITY_XPM +LSM_HOOK(int, 0, mmap_region, struct vm_area_struct *vma) +#endif LSM_HOOK(int, 0, file_mprotect, struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot) LSM_HOOK(int, 0, file_lock, struct file *file, unsigned int cmd) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 64cdf4d7bfb30b2c6363e56d02ef5c9d6b2a5c53..4a97b07338ba8293bf7226eb1bb1477e542037ac 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -534,6 +534,10 @@ * @prot contains the protection that will be applied by the kernel. * @flags contains the operational flags. * Return 0 if permission is granted. + * @mmap_region : + * Check permission for a mmap operation. The @file may be NULL, e,g. + * if mapping anonymous memory. + * @vma contains the memory region to mmap. * @file_mprotect: * Check permissions before changing memory access permissions. * @vma contains the memory region to modify. diff --git a/include/linux/mm.h b/include/linux/mm.h index 8b199766b8837879b247469bed0ea2359cfde71d..9ed1be47c8cb96bbdc0a9c096fe8f5d224e1ed35 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -316,6 +316,7 @@ extern unsigned int kobjsize(const void *objp); #define VM_HIGH_ARCH_BIT_4 36 /* bit only usable on 64-bit architectures */ #define VM_HIGH_ARCH_BIT_5 37 /* bit only usable on 64-bit architectures */ #define VM_HIGH_ARCH_BIT_6 38 /* bit only usable on 64-bit architectures */ +#define VM_HIGH_ARCH_BIT_7 39 /* bit only usable on 64-bit architectures */ #define VM_HIGH_ARCH_0 BIT(VM_HIGH_ARCH_BIT_0) #define VM_HIGH_ARCH_1 BIT(VM_HIGH_ARCH_BIT_1) #define VM_HIGH_ARCH_2 BIT(VM_HIGH_ARCH_BIT_2) @@ -323,6 +324,7 @@ extern unsigned int kobjsize(const void *objp); #define VM_HIGH_ARCH_4 BIT(VM_HIGH_ARCH_BIT_4) #define VM_HIGH_ARCH_5 BIT(VM_HIGH_ARCH_BIT_5) #define VM_HIGH_ARCH_6 BIT(VM_HIGH_ARCH_BIT_6) +#define VM_HIGH_ARCH_7 BIT(VM_HIGH_ARCH_BIT_7) #endif /* CONFIG_ARCH_USES_HIGH_VMA_FLAGS */ #ifdef CONFIG_MEM_PURGEABLE @@ -333,6 +335,12 @@ extern unsigned int kobjsize(const void *objp); #define VM_USEREXPTE 0 #endif /* CONFIG_MEM_PURGEABLE */ +#ifdef CONFIG_SECURITY_XPM +#define VM_XPM VM_HIGH_ARCH_7 +#else /* CONFIG_MEM_PURGEABLE */ +#define VM_XPM VM_NONE +#endif /* CONFIG_MEM_PURGEABLE */ + #ifdef CONFIG_ARCH_HAS_PKEYS # define VM_PKEY_SHIFT VM_HIGH_ARCH_BIT_0 # define VM_PKEY_BIT0 VM_HIGH_ARCH_0 /* A protection key is a 4-bit value */ @@ -2659,6 +2667,7 @@ extern unsigned long __must_check vm_mmap(struct file *, unsigned long, struct vm_unmapped_area_info { #define VM_UNMAPPED_AREA_TOPDOWN 1 +#define VM_UNMAPPED_AREA_XPM 2 unsigned long flags; unsigned long length; unsigned long low_limit; diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index d86bc1d2dcc3c7e03076158e5eed317431c835ec..3eb93c4870045ce60d6a096b4fd71bdbbc7a3391 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -603,6 +604,10 @@ struct mm_struct { #ifdef CONFIG_IOMMU_SUPPORT u32 pasid; #endif + +#ifdef CONFIG_SECURITY_XPM + struct xpm_region xpm_region; +#endif } __randomize_layout; /* diff --git a/include/linux/mman.h b/include/linux/mman.h index 629cefc4ecba671682408ccdfe53a0a0726dcebd..3225d2c14f87aa6b8e4d3e40a7685358a1f87b76 100644 --- a/include/linux/mman.h +++ b/include/linux/mman.h @@ -154,6 +154,7 @@ calc_vm_flag_bits(unsigned long flags) _calc_vm_trans(flags, MAP_DENYWRITE, VM_DENYWRITE ) | _calc_vm_trans(flags, MAP_LOCKED, VM_LOCKED ) | _calc_vm_trans(flags, MAP_SYNC, VM_SYNC ) | + _calc_vm_trans(flags, MAP_XPM, VM_XPM ) | arch_calc_vm_flag_bits(flags); } diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index dcf83c01f57b07b32ac115e4c5e3228120b953b2..7b3212b93d3e6b2f0ad73d935b4338735e10af77 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -145,6 +145,10 @@ enum pageflags { #endif #ifdef CONFIG_MEM_PURGEABLE PG_purgeable, +#endif +#ifdef CONFIG_SECURITY_XPM + PG_xpm_readonly, + PG_xpm_writetainted, #endif __NR_PAGEFLAGS, @@ -350,6 +354,14 @@ __PAGEFLAG(Slab, slab, PF_NO_TAIL) __PAGEFLAG(SlobFree, slob_free, PF_NO_TAIL) PAGEFLAG(Checked, checked, PF_NO_COMPOUND) /* Used by some filesystems */ +#ifdef CONFIG_SECURITY_XPM +PAGEFLAG(XPMReadonly, xpm_readonly, PF_HEAD) +PAGEFLAG(XPMWritetainted, xpm_writetainted, PF_HEAD) +#else +PAGEFLAG_FALSE(XPMReadonly) +PAGEFLAG_FALSE(XPMWritetainted) +#endif + /* Xen */ PAGEFLAG(Pinned, pinned, PF_NO_COMPOUND) TESTSCFLAG(Pinned, pinned, PF_NO_COMPOUND) @@ -843,11 +855,18 @@ static inline void ClearPageSlabPfmemalloc(struct page *page) * Flags checked when a page is freed. Pages being freed should not have * these flags set. It they are, there is a problem. */ +#ifdef CONFIG_SECURITY_XPM +#define __XPM_PAGE_FLAGS (1UL << PG_xpm_readonly | 1UL << PG_xpm_writetainted) +#else +#define __XPM_PAGE_FLAGS 0 +#endif + #define PAGE_FLAGS_CHECK_AT_FREE \ (1UL << PG_lru | 1UL << PG_locked | \ 1UL << PG_private | 1UL << PG_private_2 | \ 1UL << PG_writeback | 1UL << PG_reserved | \ 1UL << PG_slab | 1UL << PG_active | \ + __XPM_PAGE_FLAGS | \ 1UL << PG_unevictable | __PG_MLOCKED) /* diff --git a/include/linux/security.h b/include/linux/security.h index e9b4b541061477c127cd89d9125ed6856534e10e..f940ab809ad1b3795c2d362d8de2e26785f8a062 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -2002,4 +2002,13 @@ static inline int security_perf_event_write(struct perf_event *event) #endif /* CONFIG_SECURITY */ #endif /* CONFIG_PERF_EVENTS */ +#if IS_ENABLED(CONFIG_SECURITY) && IS_ENABLED(CONFIG_SECURITY_XPM) +extern int security_mmap_region(struct vm_area_struct *vma); +#else +static inline int security_mmap_region(struct vm_area_struct *vma) +{ + return 0; +} +#endif /* CONFIG_SECURITY */ + #endif /* ! __LINUX_SECURITY_H */ diff --git a/include/linux/xpm.h b/include/linux/xpm.h new file mode 100644 index 0000000000000000000000000000000000000000..2373d9c2321c13aca6e0544454f4650214b5397f --- /dev/null +++ b/include/linux/xpm.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ + +#ifndef _XPM_H +#define _XPM_H + +#include +#include +#include +#include +#include + +/** + * when inodes are destroyed, the corresponding cache must be destroyed + */ +static inline void xpm_delete_cache_node_hook(struct inode *file_node) +{ + CALL_HCK_LITE_HOOK(xpm_delete_cache_node_lhck, file_node); +} + +/** + * check whether input address range is out of the xpm region + */ +static inline bool xpm_region_outer_hook(unsigned long addr_start, + unsigned long addr_end, unsigned long flags) +{ + bool ret = true; + + CALL_HCK_LITE_HOOK(xpm_region_outer_lhck, addr_start, + addr_end, flags, &ret); + return ret; +} + +/** + * get unmapped area in xpm region + */ +static inline unsigned long xpm_get_unmapped_area_hook(unsigned long addr, + unsigned long len, unsigned long map_flags, + unsigned long unmapped_flags) +{ + unsigned long ret = 0; + + CALL_HCK_LITE_HOOK(xpm_get_unmapped_area_lhck, addr, len, + map_flags, unmapped_flags, &ret); + return ret; +} + +/* + * check the confliction of a page's xpm flags, make sure a process will + * not map any RO page into a writable vma or a WT page into a execuable/XPM + * memory region. + */ +static inline vm_fault_t xpm_integrity_check_hook(struct vm_area_struct *vma, + unsigned int vflags, unsigned long addr, struct page *page) +{ + vm_fault_t ret = 0; + + CALL_HCK_LITE_HOOK(xpm_integrity_check_lhck, vma, vflags, + addr, page, &ret); + return ret; +} + +static inline +vm_fault_t xpm_integrity_validate_hook(struct vm_area_struct *vma, + unsigned int vflags, unsigned long addr, struct page *page) +{ + vm_fault_t ret = 0; + + CALL_HCK_LITE_HOOK(xpm_integrity_validate_lhck, vma, vflags, + addr, page, &ret); + return ret; +} + +static inline +void xpm_integrity_update_hook(struct vm_area_struct *vma, + unsigned int vflags, struct page *page) +{ + CALL_HCK_LITE_HOOK(xpm_integrity_update_lhck, vma, vflags, page); +} + +static inline bool xpm_integrity_check_one_page_merge(struct page *page, + struct page *kpage) +{ + bool ret = true; + + CALL_HCK_LITE_HOOK(xpm_integrity_equal_lhck, page, kpage, &ret); + return ret; +} + +#ifdef CONFIG_ARM64 +#define pte_user_mkexec(oldpte, ptent) \ + ((!pte_user_exec(oldpte) && pte_user_exec(ptent))) +#else +#define pte_user_mkexec(oldpte, ptent) 1 +#endif + +#endif /* _XPM_H */ diff --git a/include/linux/xpm_types.h b/include/linux/xpm_types.h new file mode 100644 index 0000000000000000000000000000000000000000..22a58c8a8542dcf9675fdf4dfad52f1080a67fc6 --- /dev/null +++ b/include/linux/xpm_types.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ + +#ifndef _XPM_TYPES_H +#define _XPM_TYPES_H + +#include + +struct xpm_region { + unsigned long addr_start; /* start adress of xpm region */ + unsigned long addr_end; /* end address of xpm region */ +}; + +#endif /* _XPM_TYPES_H */ \ No newline at end of file diff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h index 2332482f7df748210c509d7a4023966944b41e4b..c452bf5bd5b436277c3d61ca40c4bb08528fe549 100644 --- a/include/trace/events/mmflags.h +++ b/include/trace/events/mmflags.h @@ -61,6 +61,12 @@ #define IF_HAVE_PG_PURGEABLE(flag,string) #endif +#ifdef CONFIG_SECURITY_XPM +#define IF_HAVE_PG_XPM_INTEGRITY(flag,string) ,{1UL << flag, string} +#else +#define IF_HAVE_PG_XPM_INTEGRITY(flag,string) +#endif + #ifdef CONFIG_MMU #define IF_HAVE_PG_MLOCK(flag,string) ,{1UL << flag, string} #else @@ -114,6 +120,8 @@ {1UL << PG_swapbacked, "swapbacked" }, \ {1UL << PG_unevictable, "unevictable" } \ IF_HAVE_PG_PURGEABLE(PG_purgeable, "purgeable" ) \ +IF_HAVE_PG_XPM_INTEGRITY(PG_xpm_readonly, "readonly") \ +IF_HAVE_PG_XPM_INTEGRITY(PG_xpm_writetainted, "writetained") \ IF_HAVE_PG_MLOCK(PG_mlocked, "mlocked" ) \ IF_HAVE_PG_UNCACHED(PG_uncached, "uncached" ) \ IF_HAVE_PG_HWPOISON(PG_hwpoison, "hwpoison" ) \ diff --git a/include/uapi/asm-generic/mman-common.h b/include/uapi/asm-generic/mman-common.h index f94f65d429bea3c26bdcdc3197376916399089e9..da6cf2b78e8da33fb5b19478125655db9f441eb0 100644 --- a/include/uapi/asm-generic/mman-common.h +++ b/include/uapi/asm-generic/mman-common.h @@ -21,6 +21,7 @@ #define MAP_TYPE 0x0f /* Mask for type of mapping */ #define MAP_FIXED 0x10 /* Interpret addr exactly */ #define MAP_ANONYMOUS 0x20 /* don't use a file */ +#define MAP_XPM 0x40 /* xpm control memory */ /* 0x0100 - 0x4000 flags are defined in asm-generic/mman.h */ #define MAP_POPULATE 0x008000 /* populate (prefault) pagetables */ diff --git a/mm/ksm.c b/mm/ksm.c index 25b8362a4f89537abe7c03156d7407d9a18383c1..fbd5cca6d0482cdbeded3efceec0dc9ea7fd3971 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -41,6 +41,7 @@ #include #include "internal.h" +#include #ifdef CONFIG_NUMA #define NUMA(x) (x) @@ -1212,6 +1213,9 @@ static int try_to_merge_one_page(struct vm_area_struct *vma, if (!PageAnon(page)) goto out; + if(!xpm_integrity_check_one_page_merge(page, kpage)) + goto out; + /* * We need the page lock to read a stable PageSwapCache in * write_protect_page(). We use trylock_page() instead of diff --git a/mm/memory.c b/mm/memory.c index ea5741b3288f41e9d6e83a7b3c1d72255f2d94a2..1095af2d2757b530031ab9886fef565290255fa7 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -86,6 +86,7 @@ #include "pgalloc-track.h" #include "internal.h" +#include #if defined(LAST_CPUPID_NOT_IN_PAGE_FLAGS) && !defined(CONFIG_COMPILE_TEST) #warning Unfortunate NUMA and NUMA Balancing config, growing page-frame for last_cpupid. @@ -2942,6 +2943,7 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf) */ set_pte_at_notify(mm, vmf->address, vmf->pte, entry); update_mmu_cache(vma, vmf->address, vmf->pte); + xpm_integrity_update_hook(vma, vmf->flags, new_page); if (old_page) { /* * Only after switching the pte to the new page may @@ -3036,6 +3038,13 @@ vm_fault_t finish_mkwrite_fault(struct vm_fault *vmf) pte_unmap_unlock(vmf->pte, vmf->ptl); return VM_FAULT_NOPAGE; } + + if (unlikely(xpm_integrity_validate_hook(vmf->vma, vmf->flags, + vmf->address, vmf->page))) { + pte_unmap_unlock(vmf->pte, vmf->ptl); + return VM_FAULT_SIGSEGV; + } + wp_page_reuse(vmf); return 0; } @@ -3087,6 +3096,13 @@ static vm_fault_t wp_page_shared(struct vm_fault *vmf) return tmp; } } else { + if (unlikely(xpm_integrity_validate_hook(vmf->vma, vmf->flags, vmf->address, + vmf->page))){ + pte_unmap_unlock(vmf->pte, vmf->ptl); + put_page(vmf->page); + return VM_FAULT_SIGSEGV; + } + wp_page_reuse(vmf); lock_page(vmf->page); } @@ -3171,6 +3187,13 @@ static vm_fault_t do_wp_page(struct vm_fault *vmf) * it's dark out, and we're wearing sunglasses. Hit it. */ unlock_page(page); + + if (unlikely(xpm_integrity_validate_hook(vmf->vma, vmf->flags, vmf->address, + vmf->page))){ + pte_unmap_unlock(vmf->pte, vmf->ptl); + return VM_FAULT_SIGSEGV; + } + wp_page_reuse(vmf); return VM_FAULT_WRITE; } else if (unlikely((vma->vm_flags & (VM_WRITE|VM_SHARED)) == @@ -3485,6 +3508,11 @@ vm_fault_t do_swap_page(struct vm_fault *vmf) * before page_add_anon_rmap() and swap_free(); try_to_free_swap() * must be called after the swap_free(), or it will never succeed. */ + if (unlikely(xpm_integrity_validate_hook(vmf->vma, vmf->flags, + vmf->address, page))){ + ret = VM_FAULT_SIGSEGV; + goto out_nomap; + } inc_mm_counter_fast(vma->vm_mm, MM_ANONPAGES); dec_mm_counter_fast(vma->vm_mm, MM_SWAPENTS); @@ -3595,6 +3623,10 @@ static vm_fault_t do_anonymous_page(struct vm_fault *vmf) if (vma->vm_flags & VM_USEREXPTE) { if (do_uxpte_page_fault(vmf, &entry)) goto oom; + + if(xpm_integrity_check_hook(vma, vmf->flags, vmf->address, + pte_page(entry))) + return VM_FAULT_SIGSEGV; else goto got_page; } @@ -3673,6 +3705,10 @@ static vm_fault_t do_anonymous_page(struct vm_fault *vmf) if (vma->vm_flags & VM_PURGEABLE) uxpte_set_present(vma, vmf->address); + if(!pte_special(entry)){ + xpm_integrity_update_hook(vma, vmf->flags, page); + } + set_pte_at(vma->vm_mm, vmf->address, vmf->pte, entry); /* No need to invalidate - it was non-present before */ @@ -3930,6 +3966,11 @@ vm_fault_t alloc_set_pte(struct vm_fault *vmf, struct page *page) return VM_FAULT_NOPAGE; } + /* check the confliction of xpm integrity flags*/ + if (unlikely(xpm_integrity_validate_hook(vmf->vma, vmf->flags, + vmf->address, page))) + return VM_FAULT_SIGSEGV; + flush_icache_page(vma, page); entry = mk_pte(page, vma->vm_page_prot); entry = pte_sw_mkyoung(entry); diff --git a/mm/migrate.c b/mm/migrate.c index cf0e966a8faa62b8d0be1f24e6cf8b5b0e6b844a..7ed4eb406d1240f12d7470f1cea94f277ecec1ed 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -636,6 +636,13 @@ void migrate_page_states(struct page *newpage, struct page *page) if (page_is_idle(page)) set_page_idle(newpage); + /* Migrate the page's xpm state */ + if(PageXPMWritetainted(page)) + SetPageXPMWritetainted(newpage); + + if(PageXPMReadonly(page)) + SetPageXPMReadonly(newpage); + /* * Copy NUMA information to the new page, to prevent over-eager * future migrations of this same page. diff --git a/mm/mmap.c b/mm/mmap.c index bccc3235cd61f37f2d30dce0f1a7e1828a8dfdb3..8e47a7d0449719c8671ccbfb5f3f1a75dff3b0fd 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -1877,8 +1878,9 @@ unsigned long mmap_region(struct file *file, unsigned long addr, vma_set_anonymous(vma); } - /* Allow architectures to sanity-check the vm_flags */ - if (!arch_validate_flags(vma->vm_flags)) { + /* Allow architectures to sanity-check the vma */ + if (security_mmap_region(vma) || + !arch_validate_flags(vma->vm_flags)) { error = -EINVAL; if (file) goto close_and_free_vma; @@ -2000,8 +2002,9 @@ static unsigned long unmapped_area(struct vm_unmapped_area_info *info) /* Check if current node has a suitable gap */ if (gap_start > high_limit) return -ENOMEM; - if (gap_end >= low_limit && - gap_end > gap_start && gap_end - gap_start >= length) + if ((gap_end >= low_limit && + gap_end > gap_start && gap_end - gap_start >= length) && + (xpm_region_outer_hook(gap_start, gap_end, info->flags))) goto found; /* Visit right subtree if it looks promising */ @@ -2104,8 +2107,9 @@ static unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info) gap_end = vm_start_gap(vma); if (gap_end < low_limit) return -ENOMEM; - if (gap_start <= high_limit && - gap_end > gap_start && gap_end - gap_start >= length) + if ((gap_start <= high_limit && + gap_end > gap_start && gap_end - gap_start >= length) && + (xpm_region_outer_hook(gap_start, gap_end, info->flags))) goto found; /* Visit left subtree if it looks promising */ @@ -2187,6 +2191,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { + unsigned long xpm_addr; struct mm_struct *mm = current->mm; struct vm_area_struct *vma, *prev; struct vm_unmapped_area_info info; @@ -2195,6 +2200,10 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, if (len > mmap_end - mmap_min_addr) return -ENOMEM; + xpm_addr = xpm_get_unmapped_area_hook(addr, len, flags, 0); + if (xpm_addr) + return xpm_addr; + if (flags & MAP_FIXED) return addr; @@ -2203,7 +2212,8 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, vma = find_vma_prev(mm, addr, &prev); if (mmap_end - len >= addr && addr >= mmap_min_addr && (!vma || addr + len <= vm_start_gap(vma)) && - (!prev || addr >= vm_end_gap(prev))) + (!prev || addr >= vm_end_gap(prev)) && + (xpm_region_outer_hook(addr, addr + len, 0))) return addr; } @@ -2227,6 +2237,7 @@ arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { + unsigned long xpm_addr; struct vm_area_struct *vma, *prev; struct mm_struct *mm = current->mm; struct vm_unmapped_area_info info; @@ -2236,6 +2247,11 @@ arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr, if (len > mmap_end - mmap_min_addr) return -ENOMEM; + xpm_addr = xpm_get_unmapped_area_hook(addr, len, flags, + VM_UNMAPPED_AREA_TOPDOWN); + if (xpm_addr) + return xpm_addr; + if (flags & MAP_FIXED) return addr; @@ -2245,7 +2261,8 @@ arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr, vma = find_vma_prev(mm, addr, &prev); if (mmap_end - len >= addr && addr >= mmap_min_addr && (!vma || addr + len <= vm_start_gap(vma)) && - (!prev || addr >= vm_end_gap(prev))) + (!prev || addr >= vm_end_gap(prev)) && + (xpm_region_outer_hook(addr, addr + len, 0))) return addr; } diff --git a/mm/mprotect.c b/mm/mprotect.c index a5ba7333f16389bba61c1a5f1586227e9f245f50..d7131fcffc3b0ddcb3a15fee28a061a2a20769fa 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include "internal.h" @@ -138,6 +139,13 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, !(vma->vm_flags & VM_SOFTDIRTY))) { ptent = pte_mkwrite(ptent); } + + /* if exec added, check xpm integrity before set pte */ + if(pte_user_mkexec(oldpte, ptent) && + unlikely(xpm_integrity_validate_hook(vma, 0, addr, + vm_normal_page(vma, addr, oldpte)))) + continue; + ptep_modify_prot_commit(vma, addr, pte, oldpte, ptent); pages++; } else if (is_swap_pte(oldpte)) { diff --git a/scripts/kconfig/lexer.l b/scripts/kconfig/lexer.l index 6a08f5f147a48865535d5a12d585de580f2b4842..c7953b16e0afe058e4f9a39f7ff73f51f7a823e9 100644 --- a/scripts/kconfig/lexer.l +++ b/scripts/kconfig/lexer.l @@ -24,6 +24,7 @@ static const char *kconfig_white_list[] = { "vendor/Kconfig", "net/newip/Kconfig", + "security/xpm/Kconfig", }; static struct { diff --git a/security/Kconfig b/security/Kconfig index 9893c316da8979cb489e1ac83a85a9661de64f2e..43cd1c19a90ade762db74b7fbe09983812dc7c73 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -230,6 +230,7 @@ source "security/loadpin/Kconfig" source "security/yama/Kconfig" source "security/safesetid/Kconfig" source "security/lockdown/Kconfig" +source "security/xpm/Kconfig" source "security/integrity/Kconfig" diff --git a/security/Makefile b/security/Makefile index 3baf435de5411b2d2f5965a75faf327b4b79355b..3f01136d7b1fd4edd53c54ae863ae1c639e30717 100644 --- a/security/Makefile +++ b/security/Makefile @@ -13,6 +13,7 @@ subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown subdir-$(CONFIG_BPF_LSM) += bpf +subdir-$(CONFIG_SECURITY_XPM) += xpm # always enable default capabilities obj-y += commoncap.o @@ -32,6 +33,7 @@ obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/ obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/ obj-$(CONFIG_CGROUPS) += device_cgroup.o obj-$(CONFIG_BPF_LSM) += bpf/ +obj-$(CONFIG_SECURITY_XPM) += xpm/ # Object integrity file lists subdir-$(CONFIG_INTEGRITY) += integrity diff --git a/security/security.c b/security/security.c index 8ea826ea6167e7173130ebaf7ed45af593a3df72..6c5f9a7c6b592e936d024fd62b3d2b67cca67252 100644 --- a/security/security.c +++ b/security/security.c @@ -2575,3 +2575,10 @@ int security_perf_event_write(struct perf_event *event) return call_int_hook(perf_event_write, 0, event); } #endif /* CONFIG_PERF_EVENTS */ + +#ifdef CONFIG_SECURITY_XPM +int security_mmap_region(struct vm_area_struct *vma) +{ + return call_int_hook(mmap_region, 0, vma); +} +#endif diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 7271a0e05966f4a81ebdf231d6bc0d4cdff14325..1c6c59b92ab5c79c73352c7246bce957c01e60dd 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -250,6 +250,8 @@ struct security_class_mapping secclass_map[] = { { "open", "cpu", "kernel", "tracepoint", "read", "write", NULL } }, { "lockdown", { "integrity", "confidentiality", NULL } }, + { "xpm", + { "exec_no_sign", "exec_anon_mem", NULL } }, { NULL } };