linux 内核函数 copy_from_user和copy_to_user 介绍

前言


copy_from_user和copy_to_user这两个函数相信做内核开发的人都非常熟悉,分别是将用户空间的数据拷贝到内核空间以及将内核空间中的数据拷贝到用户空间。这两个函数一般用于系统调用中,前者将用户空间参数拷贝到内核,后者将系统用的结果返回到用户空间。

用户空间和内核空间


Linux将地址空间分为用户空间和内核空间,内核文档Documentation/arm64/memory.txt中定义了内核地址空间和用户地址空间的范围,比如4K Page +4 level页表时,地址空间分布如下:

AArch64 Linux memory layout with 4KB pages + 4 levels:

Start			End			Size		Use
-----------------------------------------------------------------------
0000000000000000	0000ffffffffffff	 256TB		user
ffff000000000000	ffffffffffffffff	 256TB		kernel

用户空间占用低地址,内核空间占用高地址。

用户态和内核态地址空间访问权限


这个问题,做个内核开发的人也一定非常清楚,用户态只能访问用户空间,内核态可以访问用户空间和内核空间。那么在ARM64架构下这是如何实现的呢?
首先,ARM64定义了多种地址转换策略,即translation regime。而Linux内核选择的地址转换策略是Non-secure EL1&0 translation regime,如下图所示:
在这里插入图片描述

从上图我们可以看出以下几点:

  • 支持two steps地址转换,即VA先转换为IPA,IPA在转换为最终的PA,当然这里也可以选择关闭IPA到PA的转换,那么相当于VA直接转换为PA,Linux中选择的就是这种方案。
  • 该地址转换策略可以用于el0和el1的地址转换,由于Linux的策略是用户态工作在el0,内核态工作在el1,所以该地址转化策略可以用于用户态和内核态的地址转换。

地址转换表TTBR


配置Non-secure EL1&0 translation regime的寄存器有两个,分别是TTBR0_EL1和TTBR1_EL1,前者用于低地址空间的转换,后者用于高地址空间的转换,也就是用户空间通过TTBR0_EL1来做地址转换,而内核空间通过TTBR1_EL1来做地址转换。这要要注意的是,ARM64知识定义了TTBR0_EL1用于低地址空间的转换,TTBR1_EL1用于高地址空间的转换,但是并没有定义低地址空间对应用户空间,高地址空间对应内核空间,这个方案是Linux自己选择的。那么这里就有一个问题,Linux是如何实现内核态可以访问用于空间和内核空间,而用户态只能访问用户空间的呢?地址转换表中有一个UXNTable属性,可以控制EL0状态下执行时是否可以使用该地址转换表。这样就很容易实现用户态只能访问低地址空间了。

copy_from_user

static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
{
	unsigned long res = n;
	kasan_check_write(to, n);

	if (access_ok(VERIFY_READ, from, n)) {
		check_object_size(to, n, false);
		res = __arch_copy_from_user(to, from, n);
	}
	if (unlikely(res))
		memset(to + (n - res), 0, res);
	return res;
}
  • 检查用户空间地址是否正确
    这一步很有必要,试想一下,如果用户做系统调用时,故意传了一个内核地址下来,如果这里不做检查,这里将是一个很大的漏洞,可以轻而易举的对内核搞破坏。
  • 将用户空间的数据拷贝到内核空间,由于内核空间可以访问用户空间的数据,所以这里的拷贝动作很容易实现。此外这里拷贝的时候貌似用到了热补丁技术,由于热补丁的内容比较多,我们暂时不做分析,后面一定会有专门的文章介绍,我们这里先明确,内核空间可以很容易的将用户空间的数据拷贝到内核空间,反过来当然也一样。

copy_to_user

static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)
{
	kasan_check_read(from, n);

	if (access_ok(VERIFY_WRITE, to, n)) {
		check_object_size(from, n, true);
		n = __arch_copy_to_user(to, from, n);
	}
	return n;
}

同copy_from_user,不在赘述。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值