我是靠谱客的博主 飘逸面包,最近开发中收集的这篇文章主要介绍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))开始执行。

最后

以上就是飘逸面包为你收集整理的copy_from_user的全部内容,希望文章能够帮你解决copy_from_user所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(25)

评论列表共有 0 条评论

立即
投稿
返回
顶部