概述
关于在驱动中 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这个异常表查询是否有修复地址,如果有,则执行修复地址。
那么问题来了。
- ex_table这个表在什么时候建立的呢?
- 修复地址是在什么时候被赋值的呢?难道是出问题时候,把出问题的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所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复