ARMv8安全世界切换深度解析(NSEL1 → EL3切换流程)

发布时间:2026/6/6 4:40:43

ARMv8安全世界切换深度解析(NSEL1 → EL3切换流程) NSEL1 → EL3 切换流程详解NSEL1非安全EL1到EL3的切换是两个世界之间通信的核心机制。非安全世界的所有请求无论是调用硬件加密引擎还是访问安全存储都必须通过这个流程进入安全世界。1.1 标准ARMv8实现1.1.1 触发条件SMC指令执行从非安全世界进入安全世界的唯一合法方式是执行SMCSecure Monitor Call指令。SMC指令是一条特权指令只能在EL1及以上异常等级执行。当非安全世界的代码执行SMC指令时会触发一个EL3异常CPU自动跳转到EL3的异常向量表。SMC指令的格式如下SMC #imm16其中imm16是一个16位的立即数用于传递SMC调用的功能号Function ID。在实际使用中通常将功能号放在X0寄存器中立即数设置为0。1.1.2 硬件自动完成的操作当CPU执行SMC指令时硬件会自动完成以下操作不需要软件干预保存程序计数器将当前PC的值SMC指令的下一条指令地址保存到ELR_EL3寄存器中。保存程序状态将当前PSTATE的值保存到SPSR_EL3寄存器中。切换异常等级将当前异常等级从EL1提升到EL3。切换安全状态将SCR_EL3.NS位清零CPU进入安全状态。屏蔽异常将PSTATE.D、PSTATE.A、PSTATE.I、PSTATE.F位置1屏蔽所有异常。跳转到异常向量表根据异常类型和VBAR_EL3的值计算出异常向量表中对应的入口地址然后跳转到该地址执行。关键要点硬件自动完成的操作是原子的不会被中断打断。所有通用寄存器X0-X30的值保持不变不会被硬件修改。异常返回地址是SMC指令的下一条指令地址而不是SMC指令本身的地址。1.1.3 EL3异常向量表入口EL3的异常向量表包含16个入口每个入口对应一种异常类型。SMC指令触发的是同步异常对应的入口是Current EL, SP_EL0, Synchronous或Current EL, SP_ELx, Synchronous具体取决于异常发生时使用的栈指针。标准ATF中EL3异常向量表的定义如下文件bl31/aarch64/runtime_exceptions.S.section .vectors, ax .align 11 .global runtime_exceptions runtime_exceptions: /* --------------------------------------------------------------------- * Current EL with SP_EL0 : 0x000 - 0x1ff * --------------------------------------------------------------------- */ .align 7 sync_exception_sp_el0: b sync_exception_sp_el0 .align 7 irq_exception_sp_el0: b irq_exception_sp_el0 .align 7 fiq_exception_sp_el0: b fiq_exception_sp_el0 .align 7 serror_exception_sp_el0: b serror_exception_sp_el0 /* --------------------------------------------------------------------- * Current EL with SP_ELx : 0x200 - 0x3ff * --------------------------------------------------------------------- */ .align 7 sync_exception_sp_elx: b smc_handler64 //SMC指令会跳转到这里 .align 7 irq_exception_sp_elx: b irq_exception_sp_elx .align 7 fiq_exception_sp_elx: b fiq_exception_sp_elx .align 7 serror_exception_sp_elx: b serror_exception_sp_elx /* --------------------------------------------------------------------- * Lower EL using AArch64 : 0x400 - 0x5ff * --------------------------------------------------------------------- */ .align 7 sync_exception_aarch64: b sync_exception_aarch64 .align 7 irq_exception_aarch64: b irq_exception_aarch64 .align 7 fiq_exception_aarch64: b fiq_exception_aarch64 .align 7 serror_exception_aarch64: b serror_exception_aarch64 /* --------------------------------------------------------------------- * Lower EL using AArch32 : 0x600 - 0x7ff * --------------------------------------------------------------------- */ .align 7 sync_exception_aarch32: b sync_exception_aarch32 .align 7 irq_exception_aarch32: b irq_exception_aarch32 .align 7 fiq_exception_aarch32: b fiq_exception_aarch32 .align 7 serror_exception_aarch32: b serror_exception_aarch32从代码中可以看到当SMC指令在EL1执行时会跳转到smc_handler64函数进行处理。1.1.4 SMC处理函数smc_handler64smc_handler64是标准ATF中处理SMC调用的核心函数它负责解析SMC调用的功能号保存非安全世界的上下文然后调用对应的处理函数。smc_handler64的执行流程如下文件bl31/aarch64/runtime_exceptions.S保存通用寄存器将X0-X29寄存器的值保存到EL3的栈中。获取SMC功能号从X0寄存器中获取SMC调用的功能号。检查功能号的合法性验证功能号是否在有效范围内。保存非安全世界的上下文将非安全世界的系统寄存器如SP_EL1、ELR_EL1、SPSR_EL1等保存到专门的上下文结构体中。调用对应的处理函数根据功能号查找对应的处理函数并调用。恢复非安全世界的上下文处理完成后从上下文结构体中恢复非安全世界的系统寄存器。恢复通用寄存器从EL3的栈中恢复X0-X29寄存器的值。执行ERET指令返回到非安全世界继续执行SMC指令的下一条指令。关键代码片段smc_handler64: /* 保存通用寄存器到栈中 */ stp x0, x1, [sp, #-16]! stp x2, x3, [sp, #-16]! // ... 保存x4-x29 /* 获取SMC功能号 */ mov x1, x0 /* 检查功能号的合法性 */ bl check_smc_fid /* 保存非安全世界的上下文 */ bl save_ns_context /* 调用对应的处理函数 */ bl handle_smc /* 恢复非安全世界的上下文 */ bl restore_ns_context /* 恢复通用寄存器 */ ldp x28, x29, [sp], #16 // ... 恢复x0-x27 /* 返回非安全世界 */ eret1.1.5 SMC调用的参数传递机制标准ARM SMC调用遵循ARM SMC Calling ConventionSMCCC规范参数和返回值通过通用寄存器传递输入参数X0寄存器传递功能号X1-X3寄存器传递最多3个参数。返回值X0寄存器传递返回状态码X1-X3寄存器传递最多3个返回值。对于需要传递大量数据的场景如加密和解密操作通常使用共享内存的方式非安全世界的代码在非安全内存中分配一块物理连续的缓冲区。将缓冲区的物理地址和大小通过X1和X2寄存器传递给EL3。EL3的代码将该物理地址映射到EL3的虚拟地址空间然后访问缓冲区中的数据。处理完成后将结果写回缓冲区然后返回非安全世界。安全注意事项EL3的代码必须严格验证输入参数的合法性防止缓冲区溢出和指针越界。共享内存必须是物理连续的并且通过TZC-400配置为两个世界都可访问。处理完成后必须清除共享内存中的敏感数据防止信息泄露。2.2 MT8766定制实现MT8766对标准SMC调用机制做了大量的定制化修改以适应其合并的preloader.img架构。2.2.1 MTK SMC调用规范MTK遵循SMCCC规范但定义了大量自己的SIPSilicon ProviderSMC命令用于实现MTK特有的功能。MTK的SMC功能号通常使用MTK_SIP_SMC_CMD宏定义格式如下#define MTK_SIP_SMC_CMD(cmd) \ ((0x82000000) | ((cmd) 0xFFFF))其中0x82000000是MTK的SIP服务编号cmd是具体的命令号。例如MTK音频驱动使用的SMC命令定义如下文件sound/soc/mediatek/common/mtk-base-afe.h#define MTK_SIP_AUDIO_CONTROL MTK_SIP_SMC_CMD(0x517) enum mtk_audio_smc_call_op { MTK_AUDIO_SMC_OP_INIT 0, MTK_AUDIO_SMC_OP_SET_SRAM_STATE, MTK_AUDIO_SMC_OP_GET_SRAM_STATE, // ... 其他命令 };2.2.2 Preloader中的SMC处理入口由于MT8766将BL2和BL31合并为preloader.img并且全程运行在EL3所以SMC处理函数直接编译在preloader.img中而不是单独的bl31.bin中。MT8766的SMC处理函数定义在plat/mediatek/common/mtk_smc_handlers.c文件中核心函数是uintptr_t mtk_smc_handler(uint32_t smc_fid, uintptr_t x1, uintptr_t x2, uintptr_t x3, uintptr_t x4, void *cookie, void *handle) { uintptr_t ret 0; switch (smc_fid) { case MTK_SIP_AUDIO_CONTROL: ret mtk_audio_smc_handler(x1, x2, x3, x4); break; case MTK_SIP_POWER_CONTROL: ret mtk_power_smc_handler(x1, x2, x3, x4); break; case MTK_SIP_SECURITY_CONTROL: ret mtk_security_smc_handler(x1, x2, x3, x4); break; // ... 其他SMC命令处理 default: ret SMC_UNK; break; } return ret; }2.2.3 与标准实现的差异MT8766的SMC实现与标准ATF有以下几个关键差异处理函数位置不同标准ATF的SMC处理函数在bl31.bin中而MT8766的在preloader.img中。上下文保存方式不同标准ATF会完整保存非安全世界的上下文而MT8766只保存必要的寄存器以提高性能。命令范围不同MT8766定义了大量自己的SIP命令用于实现MTK特有的硬件控制功能。安全检查不同MT8766对SMC命令的安全检查更加严格很多命令只能在特定的安全状态下执行。2.3 安全风险与漏洞分析NSEL1→EL3切换流程是整个系统最容易受到攻击的环节之一。如果EL3的SMC处理函数存在漏洞攻击者可以从非安全世界发起攻击获取EL3的完全控制权进而攻破整个系统的安全防线。2.3.1 常见漏洞类型输入验证不足SMC处理函数没有严格验证输入参数的合法性导致缓冲区溢出、指针越界等漏洞。整数溢出在计算缓冲区大小或偏移量时发生整数溢出导致内存越界访问。空指针引用没有检查指针是否为NULL导致空指针引用漏洞。信息泄露处理函数没有正确清除返回值寄存器中的敏感信息导致信息泄露。权限提升处理函数没有正确检查调用者的权限允许低权限代码执行高权限操作。2.3.2 实际漏洞案例CVE-2019-2215CVE-2019-2215是高通QSEE TEE中的一个严重漏洞允许攻击者从非安全世界的用户态直接提权到TEE内核。该漏洞的根本原因是QSEE的SMC处理函数没有正确验证输入参数的长度导致缓冲区溢出。漏洞的利用过程如下攻击者在非安全世界的用户态执行SMC指令传递一个超长的参数。QSEE的SMC处理函数没有检查参数长度将超长的参数复制到固定大小的栈缓冲区中。缓冲区溢出覆盖了栈上的返回地址。攻击者控制了程序执行流执行任意代码获取TEE内核的完全控制权。这个漏洞影响了全球数十亿台安卓设备充分说明了SMC处理函数输入验证的重要性。2.3.3 安全加固建议为了防止SMC处理函数中的漏洞建议采取以下安全加固措施严格验证所有输入参数检查参数的范围、长度、对齐方式等确保所有输入都是合法的。使用栈保护机制启用编译器的栈保护选项如-fstack-protector-strong防止栈溢出攻击。最小权限原则每个SMC命令只授予完成其功能所需的最小权限。敏感信息清零处理完成后清除所有包含敏感信息的寄存器和内存缓冲区。代码审计对SMC处理函数进行严格的代码审计确保没有安全漏洞。仅仅是个人理解如有错误麻烦指正

相关新闻