专栏首页arm内核armv8/arm64 PAN深入分析
原创

armv8/arm64 PAN深入分析

1、armv8 PAN指的是内核态不能访问用户态的数据,如果内核态想访问用户态的数据,需要copy_from_user,copy_to_user。

2、那么PAN是如何实现的呢?

通过CONFIG_ARM64_SW_TTBR0_PAN来配置是否开启PAN。

./arch/arm64/include/asm/uaccess.h

#ifdef CONFIG_ARM64_SW_TTBR0_PAN
static inline void __uaccess_ttbr0_disable(void)
{
	unsigned long flags, ttbr;

	local_irq_save(flags);
	ttbr = read_sysreg(ttbr1_el1);
	ttbr &= ~TTBR_ASID_MASK;
	/* reserved_ttbr0 placed at the end of swapper_pg_dir */
	write_sysreg(ttbr + SWAPPER_DIR_SIZE, ttbr0_el1); //ttbr_el1被置为无效,无法访问用户态的页表
	isb();
	/* Set reserved ASID */
	write_sysreg(ttbr, ttbr1_el1);
	isb();
	local_irq_restore(flags);
}

static inline void __uaccess_ttbr0_enable(void)
{
	unsigned long flags, ttbr0, ttbr1;

	/*
	 * Disable interrupts to avoid preemption between reading the 'ttbr0'
	 * variable and the MSR. A context switch could trigger an ASID
	 * roll-over and an update of 'ttbr0'.
	 */
	local_irq_save(flags);
	ttbr0 = READ_ONCE(current_thread_info()->ttbr0);//正确的用户态页表基地址保存在thread_info里面

	/* Restore active ASID */
	ttbr1 = read_sysreg(ttbr1_el1);
	ttbr1 &= ~TTBR_ASID_MASK;		/* safety measure */
	ttbr1 |= ttbr0 & TTBR_ASID_MASK;
	write_sysreg(ttbr1, ttbr1_el1);
	isb();

	/* Restore user page table */
	write_sysreg(ttbr0, ttbr0_el1);  //恢复正确的用户态页表基地址,可以访问用户态的页表
	isb();
	local_irq_restore(flags);
}

static inline bool uaccess_ttbr0_disable(void)
{
	if (!system_uses_ttbr0_pan())
		return false;
	__uaccess_ttbr0_disable();
	return true;
}

static inline bool uaccess_ttbr0_enable(void)
{
	if (!system_uses_ttbr0_pan())
		return false;
	__uaccess_ttbr0_enable();
	return true;
}
#else
static inline bool uaccess_ttbr0_disable(void)
{
	return false;
}

static inline bool uaccess_ttbr0_enable(void)
{
	return false;
}
#endif

uaccess_ttbr0_disable禁止内核态向用户态的访问,原理是将ttbr0_el1寄存器置为0,ttbr0_el1实际上保存的是一级页表的地址,所以ttbr0_el1被置零以后,内存页寻址失败,PAN生效。

uaccess_ttbr0_enable从thread_info中读取正确的用户态页表机制,然后恢复ttbr0_el1,这样就可以访问用户态的页表了,PAN失效。

3、什么时候调用这两个函数呢?

1)当用户态发生中断、异常、系统调用kernel_entry时;返回用户态kernel_exit时。

./arch/arm64/kernel/entry.S

kernel_entry:
....
alternative_if ARM64_HAS_PAN
        b       1f                              // skip TTBR0 PAN
alternative_else_nop_endif

        .if     \el != 0
        mrs     x21, ttbr0_el1
        tst     x21, #TTBR_ASID_MASK            // Check for the reserved ASID
        orr     x23, x23, #PSR_PAN_BIT          // Set the emulated PAN in the saved SPSR
        b.eq    1f                              // TTBR0 access already disabled
        and     x23, x23, #~PSR_PAN_BIT         // Clear the emulated PAN in the saved SPSR
        .endif

        __uaccess_ttbr0_disable x21
1:

....

此时el为0,一定会调用uaccess_ttbr0_disable来禁止访问用户态(PAN生效)。

kernel_exit:
....
alternative_if ARM64_HAS_PAN
        b       2f                              // skip TTBR0 PAN
alternative_else_nop_endif

        .if     \el != 0
        tbnz    x22, #22, 1f                    // Skip re-enabling TTBR0 access if the PSR_PAN_BIT is set
        .endif

        __uaccess_ttbr0_enable x0, x1

        .if     \el == 0
        /*
         * Enable errata workarounds only if returning to user. The only
         * workaround currently required for TTBR0_EL1 changes are for the
         * Cavium erratum 27456 (broadcast TLBI instructions may cause I-cache
         * corruption).
         */
        bl      post_ttbr_update_workaround
        .endif
1:

....

此时el为0,一定会调用uaccess_ttbr0_enable来允许访问用户态(PAN失效)。因为已经返回了用户态,用户态当然可以访问用户态的页表了。

2)当内核态发生中断、系统调用kernel_entry时;返回内核态kernel_exit时。

如上面代码,kernel_entry,此时el为1,此时如果已经设置了PAN生效(用户态到内核态时设置的),则不会重新设置。且spsr的PAN标志位置1。

kernel_exit,此时el为1,如果spsr的PAN标志位为1,则不需要使PAN失效,因为此时处于内核态,PAN还应该生效。

3)copy_from_user、copy_to_user

ENTRY(__arch_copy_to_user)
        uaccess_enable_not_uao x3, x4, x5
        add     end, x0, x2
#include "copy_template.S"
        uaccess_disable_not_uao x3, x4
        mov     x0, #0
        ret
ENDPROC(__arch_copy_to_user)
ENTRY(__arch_copy_from_user)
        uaccess_enable_not_uao x3, x4, x5
        add     end, x0, x2
#include "copy_template.S"
        uaccess_disable_not_uao x3, x4
        mov     x0, #0                          // Nothing to copy
        ret
ENDPROC(__arch_copy_from_user)

在和用户态交换数据时,先使PAN失效(可以访问用户态),再使PAN生效(不可以访问用户态)。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • armv8/arm64 中断/系统调用流程

    1、armv8中断、系统调用的入口在arch/arm64/kernel/entry.S

    jltxgcy
  • shell变量$(CURDIR),$0,$1,$2,$#含义解释

    $(CURDIR):   CURDIR是make的内嵌变量, 为当前目录 实例 SRCTREE := $(CURDIR) *$(CURDIR)为当前目...

    张诺谦
  • [android] android通信协议

    4.2简单代码处理:时间戳(SimpleDateFormat)+随机值(Random)

    陶士涵
  • 编程小白 | 每日一练(142)

    这道理放在编程上也一并受用。在编程方面有着天赋异禀的人毕竟是少数,我们大多数人想要从编程小白进阶到高手,需要经历的是日积月累的学习,那么如何学习呢?当然是每天都...

    C语言入门到精通
  • 算法图解笔记 - 算法简介

    数组和链表的操作的运行时间 操作数组链表 读取 O(1) O(n) 插入 O(n) O(1) 删除 O(n) O(1) 选择排序 ...

    悟空聊架构
  • RFC821 简单邮件传输协议(SMTP)

    苦叶子
  • spring boot 登录注册 demo (四) -- 体验小结

    之前没有折腾过Spring,直接上来怼Spring Boot异常痛苦,参考着官网的guide(https://spring.io/guides)写了几个demo...

    千往
  • kaggle-识别手写数字

    用户1733462
  • 移动端web页面开发的一些问题

    前端涉及到的领域不单单只是PC浏览器了,现在是移动为王的时代,所以大部分的时候还是在做移动端的页面适配。所以这里记录一下在移动端开发的时候遇到的一些问题。

    踏浪
  • 数据结构Stack

    ​ 在很多应用中,我们需要维护多个对象的集合,这种操作非常简单。我们可能想要向集合中 加入某个元素,去掉某个元素,以及遍历 集合中的元素并对他们执行某种操...

    lwen

扫码关注云+社区

领取腾讯云代金券