From d733bba525cb5bbdc5975e9e05a47312341b3cbf Mon Sep 17 00:00:00 2001 From: Haryslee Date: Mon, 3 Jul 2023 17:12:19 +0800 Subject: [PATCH] feat: Support PAC(Pointer Authentication Code) to protect key data for linux kernel Signed-off-by: Haryslee Change-Id: I0061161aa1a500411c08868e54257d135b0387fc --- LICENSE | 1 + OAT.xml | 3 + pac/Makefile | 12 ++ pac/README_zh.md | 67 +++++++ pac/apply_pac.sh | 28 +++ pac/config/config.txt | 51 +++++ pac/figures/key.png | Bin 0 -> 25232 bytes pac/figures/pac.png | Bin 0 -> 29774 bytes pac/figures/pac_context.png | Bin 0 -> 17250 bytes pac/include/asm_pointer_auth_context.h | 168 +++++++++++++++++ pac/include/asm_pointer_auth_key.h | 63 +++++++ pac/include/pointer_auth_common.h | 87 +++++++++ pac/include/pointer_auth_context.h | 117 ++++++++++++ pac/src/asm_pointer_auth_constructors.S | 26 +++ pac/src/asm_pointer_auth_context.S | 240 ++++++++++++++++++++++++ pac/src/asm_pointer_auth_key.S | 56 ++++++ pac/src/pointer_auth_context.c | 95 ++++++++++ pac/src/pointer_auth_key.c | 10 + 18 files changed, 1024 insertions(+) create mode 100644 pac/Makefile create mode 100644 pac/README_zh.md create mode 100755 pac/apply_pac.sh create mode 100755 pac/config/config.txt create mode 100755 pac/figures/key.png create mode 100755 pac/figures/pac.png create mode 100755 pac/figures/pac_context.png create mode 100644 pac/include/asm_pointer_auth_context.h create mode 100644 pac/include/asm_pointer_auth_key.h create mode 100644 pac/include/pointer_auth_common.h create mode 100644 pac/include/pointer_auth_context.h create mode 100644 pac/src/asm_pointer_auth_constructors.S create mode 100644 pac/src/asm_pointer_auth_context.S create mode 100644 pac/src/asm_pointer_auth_key.S create mode 100644 pac/src/pointer_auth_context.c create mode 100644 pac/src/pointer_auth_key.c diff --git a/LICENSE b/LICENSE index fc48fd2..2016ff8 100644 --- a/LICENSE +++ b/LICENSE @@ -2,5 +2,6 @@ ./newip/ ./xpm/ ./qos_auth/ + ./pac/ As for the specific use of the licenses, please refer to the relevant description in the documents. diff --git a/OAT.xml b/OAT.xml index 9de7aac..a9a37b9 100644 --- a/OAT.xml +++ b/OAT.xml @@ -60,9 +60,11 @@ Note:If the text contains special characters, please escape them according to th + + @@ -81,6 +83,7 @@ Note:If the text contains special characters, please escape them according to th + diff --git a/pac/Makefile b/pac/Makefile new file mode 100644 index 0000000..6fbb431 --- /dev/null +++ b/pac/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2023 Huawei Device Co., Ltd. +# +# Makefile for the Linux PAC module. +# + +obj-$(CONFIG_ARM64_PTR_AUTH_EXT) += src/pointer_auth_key.o +obj-$(CONFIG_ARM64_PTR_AUTH_EXT) += src/asm_pointer_auth_key.o +obj-$(CONFIG_ARM64_PTR_AUTH_DATA_FIELD) += src/pointer_auth_context.o +obj-$(CONFIG_ARM64_PTR_AUTH_DATA_FIELD) += src/asm_pointer_auth_context.o +obj-$(CONFIG_CONSTRUCTORS) += src/asm_pointer_auth_constructors.o diff --git a/pac/README_zh.md b/pac/README_zh.md new file mode 100644 index 0000000..0ccf2fe --- /dev/null +++ b/pac/README_zh.md @@ -0,0 +1,67 @@ +## 背景 + +现阶段,内存安全漏洞是对计算机系统安全最严重的威胁。从漏洞利用的角度分析,借助常见的攻击手段,包括利用缓冲区溢出等实现JOP/ROP攻击,攻击者可以实现控制流劫持,最终导致任意代码执行,这严重摧毁了一个系统的安全性。另一方面,在攻击者利用内存漏洞做提权的过程中,通常会修改指向关键数据(如Cred)的数据指针,这类攻击被归纳为DOP攻击,同样会对系统安全产生严重威胁。 +因此,系统需要高效的漏洞防利用机制,以保护系统的控制流完整性和数据流完整性,这对于提升系统的安全竞争力具有重要意义。当前,从安全性角度,对于系统安全主流的JOP/ROP/DOP攻击,PAC机制均能实现有效防护。 + +## PAC(Pointer Authentication Code)模块 + +PAC模块利用ARMv8.3-a架构提供的PAC特性,基于linux内核提供PAC相关的特性支持,包括密钥管理、数据和指针签名验签,以及任务切换上下文、异常中断上下文的PAC保护机制。PAC主要功能实现如下: + +### 1.密钥管理 + +ARMv8.3-a指令集引入了硬件安全特性PAC,用于保护内存中指针和其他数据的完整性。原理是:ARM硬件基于QARMA密码算法,将被保护的指针或数据、用于签名的密钥和盐值作为输入,计算输出一个MAC值,对于指针来说,将有效的MAC值存放在指针的未使用高位,对于数据来说,需要另开辟内存用于保存MAC值。 + +PAC原理 + +Linux内核的密钥管理分为用户态密钥和内核态密钥,示意图如下: + +![密钥管理](figures/key.png) + +### 2.关键数据和指针保护 + +Linux内核可以利用PAC模块对任务切换上下文、异常中断上下文中的关键字段进行保护,防止在上下文切换的过程中,攻击者利用内存漏洞提权修改关键字段,最终导致任意代码执行。 + +![linux context保护](figures/pac_context.png) + +## 目录 + +PAC主要代码目录结构如下: + +``` +# 代码路径 /kernel/linux/common_modules/pac +├── config # 关键数据、指针标记配置文件 +├── figures # ReadMe 内嵌图例 +├── include # PAC头文件 +├── src # PAC代码 +└── Makefile +``` + +## 配置指导 + +1. PAC使能 + + `CONFIG_ARM64_PTR_AUTH=y` + +2. 密钥使能 + + `CONFIG_ARM64_PTR_AUTH_USER=y` + + `CONFIG_ARM64_PTR_AUTH_KERNEL=y` + + `CONFIG_ARM64_PTR_AUTH_EXT=y` + +3. 关键数据和指针保护使能 + + `CONFIG_ARM64_PTR_AUTH_PTR=y` + + `CONFIG_ARM64_PTR_AUTH_FIELD=y` + +## 相关仓 + +[内核子系统](https://gitee.com/openharmony/docs/blob/master/zh-cn/readme/%E5%86%85%E6%A0%B8%E5%AD%90%E7%B3%BB%E7%BB%9F.md) + +[kernel_linux_5.10](https://gitee.com/openharmony/kernel_linux_5.10) + +[kernel_linux_config](https://gitee.com/openharmony/kernel_linux_config) + +[kernel_linux_build](https://gitee.com/openharmony/kernel_linux_build) diff --git a/pac/apply_pac.sh b/pac/apply_pac.sh new file mode 100755 index 0000000..7192d87 --- /dev/null +++ b/pac/apply_pac.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2023 Huawei Device Co., Ltd. +# + +set -e + +OHOS_SOURCE_ROOT=$1 +KERNEL_BUILD_ROOT=$2 +PRODUCT_NAME=$3 +KERNEL_VERSION=$4 +PAC_SOURCE_ROOT=$OHOS_SOURCE_ROOT/kernel/linux/common_modules/pac + +function main() +{ + pushd . + + if [ ! -d " $KERNEL_BUILD_ROOT/arch/arm64/pac" ]; then + mkdir $KERNEL_BUILD_ROOT/arch/arm64/pac + fi + + cd $KERNEL_BUILD_ROOT/arch/arm64/pac + ln -s -f $(realpath --relative-to=$KERNEL_BUILD_ROOT/arch/arm64/pac/ $PAC_SOURCE_ROOT)/* ./ + + popd +} + +main diff --git a/pac/config/config.txt b/pac/config/config.txt new file mode 100755 index 0000000..3793f5e --- /dev/null +++ b/pac/config/config.txt @@ -0,0 +1,51 @@ +struct.task_struct mm +struct.task_struct stack +struct.task_struct real_parent +struct.task_struct sched_task_group +struct.task_struct files +struct.task_struct nsproxy +struct.task_struct ptracer_cred +struct.task_struct real_cred +struct.task_struct cred +struct.task_struct security +struct.task_struct splice_pipe +struct.task_struct fs +struct.cred session_keyring +struct.cred process_keyring +struct.cred thread_keyring +struct.cred request_key_auth +struct.cred security +struct.cred user +struct.cred user_ns +struct.cred group_info +struct.super_block s_root +struct.super_block s_xattr +struct.super_block s_user_ns +struct.super_block s_d_op +struct.super_block s_vop +struct.super_block s_security +struct.mount mnt_parent +struct.mount mnt_mountpoint +struct.mount mnt_mp +struct.mount mnt_ns +struct.vfsmount mnt_root +struct.vfsmount mnt_sb +struct.inode i_op +struct.inode i_sb +struct.inode i_verity_info +struct.inode i_security +struct.inode i_mapping +struct.dentry d_parent +struct.dentry d_inode +struct.dentry d_op +struct.dentry d_sb +struct.selinux_state status_page +struct.selinux_state avc +struct.selinux_state policy +struct.ipc_namespace user_ns +struct.shmid_kernel shm_file +struct.file f_security +struct.msg_msg security +struct.kern_ipc_perm security +struct.lsm_info blobs +struct.sock_fprog filter diff --git a/pac/figures/key.png b/pac/figures/key.png new file mode 100755 index 0000000000000000000000000000000000000000..10701d465fd63e0dad8393d259c1fb2f1751737d GIT binary patch literal 25232 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 literal 0 HcmV?d00001 diff --git a/pac/figures/pac.png b/pac/figures/pac.png new file mode 100755 index 0000000000000000000000000000000000000000..81556202ea13d6bd3a19a282865a12d1604ca55e GIT binary patch literal 29774 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 literal 0 HcmV?d00001 diff --git a/pac/include/asm_pointer_auth_context.h b/pac/include/asm_pointer_auth_context.h new file mode 100644 index 0000000..bc8acc7 --- /dev/null +++ b/pac/include/asm_pointer_auth_context.h @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ + +#ifndef __ASM_POINTER_AUTH_CONTEXT_H +#define __ASM_POINTER_AUTH_CONTEXT_H + +#include +#include + + /* Compute and store hash value of cpu context. */ + .macro sign_thread_context_common, tmp1=x0, tmp2=x1, tmp3=x2 + pacga \tmp2, \tmp1, \tmp2 + pacga \tmp2, \tmp3, \tmp2 + str \tmp2, [\tmp1, CPU_CONTEXT_PAC_HASH] + .endm + + /* Compute and auth hash value of cpu context. */ + .macro auth_thread_context_common, tmp1=x0, tmp2=x1, tmp3=x2 + pacga \tmp2, \tmp1, \tmp2 + pacga \tmp2, \tmp3, \tmp2 + ldr \tmp3, [\tmp1, CPU_CONTEXT_PAC_HASH] + cmp \tmp2, \tmp3 + b.ne .Lthread_context_pac_panic\@ + b .Lauth_thread_context_done\@ +.Lthread_context_pac_panic\@: + adrp x0, .Lthread_context_pac_str + add x0, x0, :lo12:.Lthread_context_pac_str + bl panic +.Lauth_thread_context_done\@: + .endm + + /* Compute and store hash value of the regs. */ + .macro sign_exception_context_common, tmp1=x0, tmp2=x1, tmp3=x2, tmp4=x3, tmp5=x4, tmp6=x5, tmp7=x6 + pacga \tmp2, \tmp1, \tmp2 + pacga \tmp2, \tmp3, \tmp2 + pacga \tmp2, \tmp4, \tmp2 + pacga \tmp2, \tmp5, \tmp2 + pacga \tmp2, \tmp6, \tmp2 + pacga \tmp2, \tmp7, \tmp2 + str \tmp2, [\tmp1, S_PAC_HASH] + .endm + + /* Compute and auth hash value of the regs. */ + .macro auth_exception_context_common, tmp1=x0, tmp2=x1, tmp3=x2, tmp4=x3, tmp5=x4, tmp6=x5, tmp7=x6 + pacga \tmp2, \tmp1, \tmp2 + pacga \tmp2, \tmp3, \tmp2 + pacga \tmp2, \tmp4, \tmp2 + pacga \tmp2, \tmp5, \tmp2 + pacga \tmp2, \tmp6, \tmp2 + pacga \tmp2, \tmp7, \tmp2 + ldr \tmp3, [\tmp1, S_PAC_HASH] + cmp \tmp2, \tmp3 + b.ne .Lpt_regs_pac_panic\@ + b .Lauth_exception_context_done\@ +.Lpt_regs_pac_panic\@: + adrp x0, .Lpt_regs_pac_panic_str + add x0, x0, :lo12:.Lpt_regs_pac_panic_str + bl panic +.Lauth_exception_context_done\@: + .endm + +.Lpt_regs_pac_panic_str: + .asciz "Failed to match pac hash of exception context!\n" + .align 2 + +.Lthread_context_pac_str: + .asciz "Failed to match pac hash of cpu context!\n" + .align 2 + + .macro pac_cpu_context sign_or_auth + .if \sign_or_auth == 0 + /* x0: base of curr task */ + mov x2, x0 + .else + /* x1: base of next task */ + mov x2, x1 + .endif + add x2, x2, #THREAD_CPU_CONTEXT + /* sign sp, lr of cpu context. */ + mov x3, lr + mov x4, x9 + .if \sign_or_auth == 0 + sign_thread_context_common x2, x3, x4 + .else + auth_thread_context_common x2, x3, x4 + .endif + .endm + + .macro sign_cpu_context sign=0 + pac_cpu_context \sign + .endm + + .macro auth_cpu_context auth=1 + pac_cpu_context \auth + .endm + + .macro prepare_compat_pt_regs + /* base of pt_regs */ + mov x23, sp + mov x24, #0 + mov x25, #0 + /* sign lr, sp, pc, pstate of compat task */ + mov x26, x14 + mov x27, x13 + mrs x28, elr_el1 + mov x29, x22 + .endm + + .macro prepare_pt_regs, el, sign_or_auth + /* base of pt_regs */ + mov x23, sp + /* sign x16, x17, lr, sp, pc, pstate of task */ + mov x24, x16 + mov x25, x17 + .if \sign_or_auth == 0 + mov x26, lr + .else + ldr x26, [x23, #S_LR] + .endif + .if \el == 0 + mrs x27, sp_el0 + .else + add x27, x23, #S_FRAME_SIZE + .endif + mrs x28, elr_el1 + .if \sign_or_auth == 0 + mrs x29, spsr_el1 + .else + mov x29, x22 + .endif + .endm + + .macro pac_pt_regs, el, sign_or_auth + .if \el == 0 + /* Test the task is in the mode of 32-bit or 64-bit */ + mrs x23, spsr_el1 + mov x24, #(PSR_MODE32_BIT | PSR_MODE_MASK) + mov x25, #(PSR_MODE32_BIT | PSR_MODE_EL0t) + and x23, x23, x24 + sub x23, x23, x25 + cbnz x23, .Lis_not_compat_task\@ + /* Task in 32-bit mode */ + prepare_compat_pt_regs + b .Lpac_handle\@ + .endif + /* Task in 64-bit mode */ +.Lis_not_compat_task\@: + prepare_pt_regs \el, \sign_or_auth + /* Call the sign or auth function. */ +.Lpac_handle\@: + .if \sign_or_auth == 0 + sign_exception_context_common x23, x24, x25, x26, x27, x28, x29 + .else + auth_exception_context_common x23, x24, x25, x26, x27, x28, x29 + .endif + .endm + + .macro sign_pt_regs, el, sign=0 + pac_pt_regs \el, \sign + .endm + + .macro auth_pt_regs, el, auth=1 + pac_pt_regs \el, \auth + .endm + +#endif /* __ASM_POINTER_AUTH_CONTEXT_H */ diff --git a/pac/include/asm_pointer_auth_key.h b/pac/include/asm_pointer_auth_key.h new file mode 100644 index 0000000..d49e149 --- /dev/null +++ b/pac/include/asm_pointer_auth_key.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * + * Pointer authentication keys initialisation. + */ + +#ifndef __ASM_POINTER_AUTH_KEY_H +#define __ASM_POINTER_AUTH_KEY_H + +#include +#include +#include +#include + + .macro __ptrauth_address_keys_install_kernel tmp1, tmp2, tmp3 + ldp \tmp2, \tmp3, [\tmp1, #PTRAUTH_KERNEL_KEY_APIB] + msr_s SYS_APIBKEYLO_EL1, \tmp2 + msr_s SYS_APIBKEYHI_EL1, \tmp3 + + adr_l \tmp1, kernel_common_keys + ldp \tmp2, \tmp3, [\tmp1, #PTRAUTH_KERNEL_KEY_APIA] + msr_s SYS_APIAKEYLO_EL1, \tmp2 + msr_s SYS_APIAKEYHI_EL1, \tmp3 + + ldp \tmp2, \tmp3, [\tmp1, #PTRAUTH_KERNEL_KEY_APDA] + msr_s SYS_APDAKEYLO_EL1, \tmp2 + msr_s SYS_APDAKEYHI_EL1, \tmp3 + + ldp \tmp2, \tmp3, [\tmp1, #PTRAUTH_KERNEL_KEY_APDB] + msr_s SYS_APDBKEYLO_EL1, \tmp2 + msr_s SYS_APDBKEYHI_EL1, \tmp3 + .endm + + .macro __ptrauth_generic_key_install_kernel tmp1, tmp2, tmp3 + ldp \tmp2, \tmp3, [\tmp1, #PTRAUTH_KERNEL_KEY_APGA] + msr_s SYS_APGAKEYLO_EL1, \tmp2 + msr_s SYS_APGAKEYHI_EL1, \tmp3 + .endm + + .macro ptrauth_keys_install_kernel_all tsk, tmp1, tmp2, tmp3 + mov \tmp1, #THREAD_KEYS_KERNEL + add \tmp1, \tsk, \tmp1 + +alternative_if_not ARM64_HAS_ADDRESS_AUTH + b .Lno_addr_auth\@ +alternative_else_nop_endif + __ptrauth_address_keys_install_kernel \tmp1, \tmp2, \tmp3 + +.Lno_addr_auth\@: +alternative_if ARM64_HAS_GENERIC_AUTH + __ptrauth_generic_key_install_kernel \tmp1, \tmp2, \tmp3 +alternative_else_nop_endif + .endm + + .macro __ptrauth_keys_install_kernel_all tsk, tmp1, tmp2, tmp3 + mov \tmp1, #THREAD_KEYS_KERNEL + add \tmp1, \tsk, \tmp1 + __ptrauth_address_keys_install_kernel \tmp1, \tmp2, \tmp3 + __ptrauth_generic_key_install_kernel \tmp1, \tmp2, \tmp3 + .endm + +#endif diff --git a/pac/include/pointer_auth_common.h b/pac/include/pointer_auth_common.h new file mode 100644 index 0000000..1eb97e1 --- /dev/null +++ b/pac/include/pointer_auth_common.h @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ + +#ifndef __POINTER_AUTH_COMMON_H__ +#define __POINTER_AUTH_COMMON_H__ + +#define PTR_AUTH_PROTECT_PTR __attribute__((pac_protected_ptr)) +#define PTR_AUTH_PROTECT_DATA __attribute__((pac_protected_data)) + +#define pauth_sign(type, key, addr, mod) \ + pauth_common(pac, type, key, addr, mod) + +#define pauth_validate(type, key, addr, mod) \ + pauth_common(aut, type, key, addr, mod) + +#define pauth_strip(type, addr) \ +({ \ + const void *__addr = (addr); \ +\ + asm ("xpac" #type " %0\n" : "+r" (__addr)); \ + (typeof (addr))__addr; \ +}) + +#define pauth_hash(addr, mod) ((unsigned int) (pauth_pacga(addr, mod) >> 32)) + +#define pauth_common(prefix, type, key, addr, mod) \ +({ \ + const void *__addr = (addr); \ + unsigned long __mod = (unsigned long)(mod); \ +\ + if (__builtin_constant_p(mod) && (__mod == 0)) \ + asm (#prefix #type "z" #key " %0\n" : "+r" (__addr)); \ + else \ + asm (#prefix #type #key " %0, %1\n" : "+r" (__addr) : \ + "r" (__mod)); \ + (typeof (addr))(__addr); \ +}) + +#define pauth_get_raw_data(addr) \ +({ \ + const void *__addr; \ + asm ("mov %0, %1\n" : "=&r" (__addr) : \ + "r" (addr)); \ + (void *)(__addr); \ +}) + +#define pauth_sign_function(fun, mod, key) \ +({ \ + const void *__fun = (fun); \ + pauth_common(pac, i, key, __fun, mod); \ + (void *)(__fun); \ +}) + +#define pauth_pacda(addr, mod) pauth_common(pac, d, a, addr, mod) + +#define pauth_pacdb(addr, mod) pauth_common(pac, d, b, addr, mod) + +#define pauth_pacia(addr, mod) pauth_common(pac, i, a, addr, mod) + +#define pauth_pacib(addr, mod) pauth_common(pac, i, b, addr, mod) + +#define pauth_pacga(addr, mod) \ +({ \ + const void *__addr = (addr); \ + unsigned long __mod = (unsigned long)(mod); \ + unsigned long __pac; \ +\ + asm ("pacga %0, %1, %2\n" : "=r" (__pac) : "r" (__addr), \ + "r" (__mod)); \ + __pac; \ +}) + +#define pauth_autda(addr, mod) pauth_common(aut, d, a, addr, mod) + +#define pauth_autdb(addr, mod) pauth_common(aut, d, b, addr, mod) + +#define pauth_autia(addr, mod) pauth_common(aut, i, a, addr, mod) + +#define pauth_autib(addr, mod) pauth_common(aut, i, b, addr, mod) + +#define pauth_xpacd(addr) pauth_strip(d, addr) + +#define pauth_xpaci(addr) pauth_strip(i, addr) + +#endif /* __POINTER_AUTH_COMMON_H__ */ diff --git a/pac/include/pointer_auth_context.h b/pac/include/pointer_auth_context.h new file mode 100644 index 0000000..5ef6813 --- /dev/null +++ b/pac/include/pointer_auth_context.h @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ + +#ifndef __POINTER_AUTH_CONTEXT_H +#define __POINTER_AUTH_CONTEXT_H + +#include +#include +#include + +enum pac_pt_regs { + REGS_X16 = 0, + REGS_X17, + REGS_LR, + REGS_SP, + REGS_PC, + REGS_PSTATE, +}; + +void sign_thread_context(void *cpu_context); +void auth_thread_context(void *cpu_context); + +void sign_exception_context_asm(void *regs); +void auth_exception_context_asm(void *regs); + +int set_exception_context_register_asm(void *regs, int offset, u64 val); + +#ifdef CONFIG_COMPAT +void sign_compat_exception_context_asm(void *regs); +void auth_compat_exception_context_asm(void *regs); + +int set_compat_exception_context_register_asm(void *regs, int offset, u64 val); +#else +static inline void sign_compat_exception_context_asm(void *regs) +{ +} + +static inline void auth_compat_exception_context_asm(void *regs) +{ +} + +static inline int set_compat_exception_context_register_asm(void *regs, int offset, u64 val) +{ + return 0; +} +#endif + +static inline void sign_compat_exception_context(void *regs) +{ + return sign_compat_exception_context_asm(regs); +} + +static inline void auth_compat_exception_context(void *regs) +{ + return auth_compat_exception_context_asm(regs); +} + +static inline void sign_exception_context(void *regs) +{ + if (compat_user_mode((struct pt_regs *)regs)) { + sign_compat_exception_context_asm(regs); + } else { + sign_exception_context_asm(regs); + } +} + +static inline void auth_exception_context(void *regs) +{ + if (compat_user_mode((struct pt_regs *)regs)) { + auth_compat_exception_context_asm(regs); + } else { + auth_exception_context_asm(regs); + } +} + +#define resign_compat_exception_context_start(regs) \ +do { \ + unsigned long irq_flags; \ + local_irq_save(irq_flags); \ + auth_compat_exception_context_asm(regs); + +#define resign_compat_exception_context_end(regs) \ + sign_compat_exception_context_asm(regs); \ + local_irq_restore(irq_flags); \ +} while(0) + +#define resign_exception_context_start(regs) \ +do { \ + unsigned long irq_flags; \ + local_irq_save(irq_flags); \ + auth_exception_context(regs); + +#define resign_exception_context_end(regs) \ + sign_exception_context(regs); \ + local_irq_restore(irq_flags); \ +} while(0) + +#define sign_exception_context_start(regs) \ +do { \ + unsigned long irq_flags; \ + local_irq_save(irq_flags); + +#define sign_exception_context_end(regs) \ + sign_exception_context(regs); \ + local_irq_restore(irq_flags); \ +} while(0) + +int set_compat_exception_context_register(void *regs, enum pac_pt_regs regs_enum, u64 val); +int set_exception_context_register(void *regs, enum pac_pt_regs regs_enum, u64 val); + +void set_compat_exception_context_register_index(struct pt_regs *regs, int index, u64 val); +void set_exception_context_register_index(struct pt_regs *regs, int index, u64 val); + +#endif /* __POINTER_AUTH_CONTEXT_H */ + diff --git a/pac/src/asm_pointer_auth_constructors.S b/pac/src/asm_pointer_auth_constructors.S new file mode 100644 index 0000000..4f1405b --- /dev/null +++ b/pac/src/asm_pointer_auth_constructors.S @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ + +#include +#include +#include + +.pushsection ".init.text", "ax" + +SYM_CODE_START(init_constructors) + mov x21, x30 + adrp x19, __ctors_end + adrp x20, __ctors_start + add x19, x19, #:lo12:__ctors_end + add x20, x20, #:lo12:__ctors_start + cmp x20, x19 + b.cs 4f +3: ldr x8, [x20], #8 + blr x8 + cmp x20, x19 + b.cc 3b +4: mov x30, x21 + ret +SYM_CODE_END(init_constructors) diff --git a/pac/src/asm_pointer_auth_context.S b/pac/src/asm_pointer_auth_context.S new file mode 100644 index 0000000..3a43703 --- /dev/null +++ b/pac/src/asm_pointer_auth_context.S @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ + +#include "asm_pointer_auth_context.h" + +#include +#include +#include + +#ifdef CONFIG_COMPAT + /* Obtain the regs of compat task to sign or authenticate. */ + .macro ldr_compat_pt_regs + mov x1, #0 + mov x2, #0 + /* load lr, sp, pc, pstate of compat task */ + ldr x3, [x0, #S_COMPAT_LR] + ldr x4, [x0, #S_COMPAT_SP] + ldr x5, [x0, #S_PC] + ldr x6, [x0, #S_PSTATE] + .endm +#endif + + /* Obtain the regs of task to sign or authenticate. */ + .macro ldr_pt_regs + /* load x16, x17, lr, sp, pc, pstate of task */ + ldp x1, x2, [x0, #S_X16] + ldr x3, [x0, #S_LR] + ldr x4, [x0, #S_SP] + ldr x5, [x0, #S_PC] + ldr x6, [x0, #S_PSTATE] + .endm + +/* + * Register sign_thread_context for AArch64. + * void sign_thread_context(struct cpu_context *cpu_context) + * On entry: + * x0: the pointer of cpu_context + */ +SYM_FUNC_START(sign_thread_context) + ldr x1, [x0, #CPU_CONTEXT_PC] + ldr x2, [x0, #CPU_CONTEXT_SP] + sign_thread_context_common + ret +SYM_FUNC_END(sign_thread_context) + +/* + * Register auth_thread_context for AArch64. + * void auth_thread_context(struct cpu_context *cpu_context) + * On entry: + * x0: the pointer of cpu_context + */ +SYM_FUNC_START(auth_thread_context) + stp x29, x30, [sp, #-16]! + mov x29, sp + ldr x1, [x0, #CPU_CONTEXT_PC] + ldr x2, [x0, #CPU_CONTEXT_SP] + auth_thread_context_common + ldp x29, x30, [sp], #16 + ret +SYM_FUNC_END(auth_thread_context) + +/* + * Register set_exception_context_register_asm for AArch64. + * int set_exception_context_register_asm(struct pt_regs *regs, int offset, u64 val); + * On entry: + * x0: the regs of task + * x1: the offset of member in pt_regs struct + * x2: the value need to be update + */ +SYM_FUNC_START(set_exception_context_register_asm) + stp x29, x30, [sp, #-16]! + mov x29, sp + mov x9, x1 + mov x10, x2 + mrs x11, daif + msr daifset, #0x2 + ldr_pt_regs + mov x12, x1 + mov x13, x2 + auth_exception_context_common x0, x12, x13 + cmp x9, #S_LR + b.eq .Lupdate_lr + b.ls .Lchoose_lower + cmp x9, #S_PC + b.eq .Lupdate_pc + b.cc .Lupdate_sp + cmp x9, #S_PSTATE + b.eq .Lupdate_pstate +.Lerror_return: + /* invalid value: return -EINVAL */ + mov x0, #-22 + b .Lreturn +.Lchoose_lower: + cmp x9, #S_X16 + b.eq .Lupdate_x16 + b.hi .Lupdate_x17 + b .Lerror_return +.Lupdate_pstate: + mov x6, x10 +.Lupdate_done: + str x10, [x0, x9] + sign_exception_context_common +.Lreturn: + mov x0, #0 + msr daif, x11 + ldp x29, x30, [sp], #16 + ret + +.Lupdate_x16: + mov x1, x10 + b .Lupdate_done +.Lupdate_x17: + mov x2, x10 + b .Lupdate_done +.Lupdate_lr: + mov x3, x10 + b .Lupdate_done +.Lupdate_sp: + mov x4, x10 + b .Lupdate_done +.Lupdate_pc: + mov x5, x10 + b .Lupdate_done +SYM_FUNC_END(set_exception_context_register_asm) + +#ifdef CONFIG_COMPAT +/* + * Register set_compat_exception_context_register_asm for AArch64. + * int set_compat_exception_context_register_asm(struct pt_regs *regs, int offset, u64 val); + * On entry: + * x0: the regs of compat task + * x1: the offset of member in pt_regs struct + * x2: the value need to be update + */ +SYM_FUNC_START(set_compat_exception_context_register_asm) + stp x29, x30, [sp, #-16]! + mov x29, sp + mov x9, x1 + mov x10, x2 + mrs x11, daif + msr daifset, #0x2 + ldr_compat_pt_regs + mov x12, x1 + mov x13, x2 + auth_exception_context_common x0, x12, x13 + cmp x9, #S_COMPAT_LR + b.eq .Lupdate_compat_lr + b.ls .Lcompat_choose_lower + cmp x9, #S_PSTATE + b.eq .Lupdate_compat_pstate + b.cc .Lupdate_compat_pc +.Lcompat_error_return: + /* invalid value: return -EINVAL */ + mov x0, #-22 + b .Lcompat_return +.Lcompat_choose_lower: + cmp x9, #S_COMPAT_SP + b.eq .Lupdate_compat_sp + b .Lcompat_error_return +.Lupdate_compat_pstate: + mov x6, x10 +.Lcompat_update_done: + str x10, [x0, x9] + sign_exception_context_common +.Lcompat_return: + mov x0, #0 + msr daif, x11 + ldp x29, x30, [sp], #16 + ret + +.Lupdate_compat_lr: + mov x3, x10 + b .Lcompat_update_done +.Lupdate_compat_sp: + mov x4, x10 + b .Lcompat_update_done +.Lupdate_compat_pc: + mov x5, x10 + b .Lcompat_update_done +SYM_FUNC_END(set_compat_exception_context_register_asm) +#endif + +/* + * Register sign_exception_context_asm for AArch64. + * void sign_exception_context_asm(struct pt_regs *regs); + * On entry: + * x0: the regs of task + */ +SYM_FUNC_START(sign_exception_context_asm) + ldr_pt_regs + sign_exception_context_common + ret +SYM_FUNC_END(sign_exception_context_asm) + +/* + * Register auth_exception_context_asm for AArch64. + * void auth_exception_context_asm(struct pt_regs *regs); + * On entry: + * x0: the regs of task + */ +SYM_FUNC_START(auth_exception_context_asm) + stp x29, x30, [sp, #-16]! + mov x29, sp + ldr_pt_regs + auth_exception_context_common + ldp x29, x30, [sp], #16 + ret +SYM_FUNC_END(auth_exception_context_asm) + +#ifdef CONFIG_COMPAT +/* + * Register sign_compat_exception_context_asm for AArch64. + * void sign_compat_exception_context_asm(struct pt_regs *regs); + * On entry: + * x0: the regs of compat task + */ +SYM_FUNC_START(sign_compat_exception_context_asm) + ldr_compat_pt_regs + sign_exception_context_common + ret +SYM_FUNC_END(sign_compat_exception_context_asm) + +/* + * Register auth_compat_exception_context_asm for AArch64. + * void auth_compat_exception_context_asm(struct pt_regs *regs); + * On entry: + * x0: the regs of compat task + */ +SYM_FUNC_START(auth_compat_exception_context_asm) + stp x29, x30, [sp, #-16]! + mov x29, sp + ldr_compat_pt_regs + auth_exception_context_common + ldp x29, x30, [sp], #16 + ret +SYM_FUNC_END(auth_compat_exception_context_asm) +#endif + diff --git a/pac/src/asm_pointer_auth_key.S b/pac/src/asm_pointer_auth_key.S new file mode 100644 index 0000000..ab2360d --- /dev/null +++ b/pac/src/asm_pointer_auth_key.S @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * + * Pointer authentication keys initialisation. + */ + +#include +#include +#include + +.pushsection ".init.text", "ax" + + .macro ptrauth_key_init type, tmp + mrs x25, ap\type\()keylo_el1 + str x25, [\tmp] + mrs x25, ap\type\()keyhi_el1 + str x25, [\tmp, #8] + .endm + + /* init ptrauth key for kernel backward-edge CFI */ + .macro ptrauth_back_key_init + mov x6, x5 /* x5: address of init task */ + mov x7, #THREAD_KEYS_KERNEL + add x6, x6, x7 + add x6, x6, #PTRAUTH_KERNEL_KEY_APIB + ptrauth_key_init ib, x6 + .endm + + /* init common ptrauth keys for kernel forward-edge CFI, data pointer DFI and data field DFI */ + .macro ptrauth_common_keys_init + adr_l x7, kernel_common_keys + mov x6, x7 + add x6, x6, #PTRAUTH_KERNEL_KEY_APIA + ptrauth_key_init ia, x6 + + mov x6, x7 + add x6, x6, #PTRAUTH_KERNEL_KEY_APDA + ptrauth_key_init da, x6 + + mov x6, x7 + add x6, x6, #PTRAUTH_KERNEL_KEY_APDB + ptrauth_key_init db, x6 + + mov x6, x7 + add x6, x6, #PTRAUTH_KERNEL_KEY_APGA + ptrauth_key_init ga, x6 + + .endm + +SYM_CODE_START(ptrauth_kernel_keys_init) + ptrauth_back_key_init + ptrauth_common_keys_init + isb + ret +SYM_CODE_END(ptrauth_kernel_keys_init) diff --git a/pac/src/pointer_auth_context.c b/pac/src/pointer_auth_context.c new file mode 100644 index 0000000..95fa3f6 --- /dev/null +++ b/pac/src/pointer_auth_context.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ + +#include +#include +#include +#include + +/* The members of arrays below are corresponding to the enum defined in pointer_auth_context.h: + * enum pac_pt_regs { + * REGS_X16 = 0, + * REGS_X17, + * REGS_LR, + * REGS_SP, + * REGS_PC, + * REGS_PSTATE, + * }; + * + * compat_regs_offset_array[]: + * S_X14: the offset of compat_lr + * S_X13: the offset of compat_sp + */ +static off_t compat_regs_offset_array[] = {0, 0, S_X14, S_X13, S_PC, S_PSTATE}; +static off_t regs_offset_array[] = {S_X16, S_X17, S_LR, S_SP, S_PC, S_PSTATE}; + +int set_compat_exception_context_register(void *regs, enum pac_pt_regs regs_enum, u64 val) +{ + switch (regs_enum) { + case REGS_LR: + case REGS_SP: + case REGS_PC: + case REGS_PSTATE: + return set_compat_exception_context_register_asm(regs, compat_regs_offset_array[regs_enum], val); + default: + return -EINVAL; + } +} + +int set_exception_context_register(void *regs, enum pac_pt_regs regs_enum, u64 val) +{ + if (compat_user_mode((struct pt_regs *)regs)) { + return set_compat_exception_context_register(regs, regs_enum, val); + } else { + switch (regs_enum) { + case REGS_X16: + case REGS_X17: + case REGS_LR: + case REGS_SP: + case REGS_PC: + case REGS_PSTATE: + return set_exception_context_register_asm(regs, regs_offset_array[regs_enum], val); + default: + return -EINVAL; + } + } +} + +void set_compat_exception_context_register_index(struct pt_regs *regs, int index, uint64_t val) +{ + /* 14 means the index of compat_lr */ + if (index == 14) { + set_compat_exception_context_register_asm(regs, S_X14, val); + /* 13 means the index of compat_sp */ + } else if (index == 13) { + set_compat_exception_context_register_asm(regs, S_X13, val); + } else { + regs->regs[index] = val; + } +} + +void set_exception_context_register_index(struct pt_regs *regs, int index, uint64_t val) +{ + off_t offset; + + if (compat_user_mode(regs)) { + set_compat_exception_context_register_index(regs, index, val); + } else { + switch (index) { + /* 16 means the index of regs[16] */ + case 16: + /* 17 means the index of regs[17] */ + case 17: + /* 30 means the index of regs[30] */ + case 30: + offset = offsetof(struct pt_regs, regs[index]); + set_exception_context_register_asm(regs, offset, val); + break; + default: + regs->regs[index] = val; + } + } +} + diff --git a/pac/src/pointer_auth_key.c b/pac/src/pointer_auth_key.c new file mode 100644 index 0000000..9293413 --- /dev/null +++ b/pac/src/pointer_auth_key.c @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * + * Pointer authentication keys initialisation. + */ + +#include + +struct ptrauth_keys_kernel_common kernel_common_keys = {{-1, -1}}; -- Gitee