copy_from_user

关于在驱动中 copy_to_user 与memcpy的区别,参考下面文章就可以了。
https://zhuanlan.zhihu.com/p/102957159

但是上面的文章,有个地方说的不太清楚,
比如说 driver中用copy_to_user时, 会将数据copy到user的地址上,如果user提供的是非法的地址,是不会引发引发内核 oops,因为内核进行了修复, 返回到用户层的read函数仅仅是打出 "bad address"信息。
详细点说的话,在copy_to_user执行时,触发pagefault, 之后触发fixup函数,查询ex_table这个异常表查询是否有修复地址,如果有,则执行修复地址。
那么问题来了。

  1. ex_table这个表在什么时候建立的呢?
  2. 修复地址是在什么时候被赋值的呢?难道是出问题时候,把出问题的user虚拟地址以及修复的地址写到异常表里吗?

带着这两个问题,分析了下 copy_to_user函数.
发现copy_to_user函数里,会把可能会出问题的指令以及对应的修复代码的头地址 存入
section. _ex_table里,
之后内核编译后,会把 .section ex_table 统一放到一个地方(链接脚本内部定义)。

  __start___ex_table = .;   //异常表
  __ex_table : { *(__ex_table) }
  __stop___ex_table = .;

copy_to_user代码如下:

/*
 * Copy to user space from a kernel buffer (alignment handled by the hardware)
 *
 * Parameters:
 *	x0 - to
 *	x1 - from
 *	x2 - n
 * Returns:
 *	x0 - bytes not copied
 */
	.macro ldrb1 ptr, regB, val
	ldrb  \ptr, [\regB], \val
	.endm

	.macro strb1 ptr, regB, val
	uao_user_alternative 9998f, strb, sttrb, \ptr, \regB, \val
	.endm

	.macro ldrh1 ptr, regB, val
	ldrh  \ptr, [\regB], \val
	.endm

	.macro strh1 ptr, regB, val
	uao_user_alternative 9998f, strh, sttrh, \ptr, \regB, \val
	.endm

	.macro ldr1 ptr, regB, val
	ldr \ptr, [\regB], \val
	.endm

	.macro str1 ptr, regB, val
	uao_user_alternative 9998f, str, sttr, \ptr, \regB, \val
	.endm

	.macro ldp1 ptr, regB, regC, val
	ldp \ptr, \regB, [\regC], \val
	.endm

	.macro stp1 ptr, regB, regC, val
	uao_stp 9998f, \ptr, \regB, \regC, \val
	.endm

end	.req	x5
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)

	.section .fixup,"ax"
	.align	2
9998:	sub	x0, end, dst			// bytes not copied
	ret
	.previous

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)

	.section .fixup,"ax"
	.align	2
9998:	sub	x0, end, dst			// bytes not copied
	ret
	.previous

前面定义了一些宏,比如说:

	.macro strb1 ptr, regB, val
	uao_user_alternative 9998f, strb, sttrb, \ptr, \regB, \val
	.endm

此宏 strb1 会在"copy_template.S"(实现真正的memcpy)中会被用到。
此宏,会操作用户层的传进来的buffer,我们可以注意到宏的内部干什么了。

uao_user_alternative 9998f, strb, sttrb, \ptr, \regB, \val

uao_user_alternative 也是一个宏

	.macro uao_user_alternative l, inst, alt_inst, reg, addr, post_inc
		alternative_if_not ARM64_HAS_UAO
8888:			\inst	\reg, [\addr], \post_inc;
			nop;
		alternative_else
			\alt_inst	\reg, [\addr];
			add		\addr, \addr, \post_inc;
		alternative_endif

		_asm_extable	8888b,\l;
	.endm

观察最后,就会发现,

_asm_extable	8888b,\l;

8888b代表:

8888:			\inst	\reg, [\addr], \post_inc;

意思就是说此操作:可能会引发pagefault(addr是用户传进来的非法地址)
即将上面这条汇编指令的标号8888:存入_asm_extable 的第一个位置 (编译后8888b会转换成地址),
后面的 l (即:9998f),即:修复地址,会存到第二个位置,而不难得知,9998f指向的位置是:

	.section .fixup,"ax"
	.align	2
9998:	sub	x0, end, dst			// bytes not copied
	ret
	.previous

即 fixup段的 9998:标号处。
总结一下,此异常表:

_asm_extable	(\inst	\reg, [\addr], \post_inc), (sub	x0, end, dst);

即:如果此指令出现问题:(\inst \reg, [\addr], \post_inc),则从 (sub x0, end, dst) 开始执行。
总结一下流程:
1 用户层 传入非法地址 ,driver层copy_to_user
2 copy_to_user在执行过程中,“copy_template.S”中进行memcpy拷贝时,执行到 ((\inst \reg, [\addr], \post_inc))指令引发pagefault(addr是user传过来的非法地址)
3 pagefault 时,回在在fixup函数中 查找 ex_table表,发现触发异常的指令地址(((\inst \reg, [\addr], \post_inc)))存在于此表中,则从表中第二项((sub x0, end, dst))开始执行。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值