您的位置 首页 动态

linux内核中的copy_to_user和copy_from_user(一)

Kernelversion:2614CPUarchitecture:ARM920TAuthor:ce123(http:blogcsdnnetce123)1copy_from_user在学习Linux内

Kernel version:2.6.14

CPU architecture:ARM920T

Author:ce123(http://blog.csdn.net/ce123)

1.copy_from_user

在学习Linux内核驱动的时分,常常会碰到copy_from_user和copy_to_user这两个函数,设备驱动程序中的ioctl函数就常常会用到。这两个函数担任在用户空间和内核空间传递数据。首要看看它们的界说(linux/include/asm-arm/uaccess.h),先看copy_from_user:

[plain]view plaincopy

print?

  1. staticinlineunsignedlongcopy_from_user(void*to,constvoid__user*from,unsignedlongn)
  2. {
  3. if(access_ok(VERIFY_READ,from,n))
  4. n=__arch_copy_from_user(to,from,n);
  5. else/*securityhole-plugit*/
  6. memzero(to,n);
  7. returnn;
  8. }

先看函数的三个参数:*to是内核空间的指针,*from是用户空间指针,n表明从用户空间想内核空间复制数据的字节数。假如成功履行复制操作,则回来0,不然回来还没有完结复制的字节数。

这个函数从结构上来剖析,其实都能够分为两个部分:

  1. 首要查看用户空间的地址指针是否有用;
  2. 调用__arch_copy_from_user函数。

1.1.access_ok

access_ok用来对用户空间的地址指针from作某种有用性查验,这个宏和体系结构相关,在arm渠道上为(linux/include/asm-arm/uaccess.h):

[plain]view plaincopy

print?

  1. #define__range_ok(addr,size)({\
  2. unsignedlongflag,sum;\
  3. __chk_user_ptr(addr);\
  4. __asm__(“adds%1,%2,%3;sbcccs%1,%1,%0;movcc%0,#0″\
  5. :”=&r”(flag),”=&r”(sum)\
  6. :”r”(addr),”Ir”(size),”0″(current_thread_info()->addr_limit)\
  7. :”cc”);\
  8. flag;})
  9. #defineaccess_ok(type,addr,size)(__range_ok(addr,size)==0)

能够看到access_ok中第一个参数type并没有用到,__range_ok的效果在于判别addr+size之后是否还在进程的用户空间规模之内。下面咱们详细看一下。这段代码涉及到GCC内联汇编,不明白的朋友能够先看看这篇博客(http://blog.csdn.net/ce123/article/details/8209702)。
(1)unsigned long flag, sum;\\界说两个变量

  • flag:保存成果的变量:非零代表地址无效,零代表地址能够拜访。初始寄存非零值(current_thread_info()->addr_limit),也便是当时进程的地址上限值。
  • sum:保存要拜访的地址规模结尾,用于和当时进程地址空间约束数据做比较。
(2)__chk_user_ptr(addr);\\界说是一个空函数
这个函数涉及到__CHECKER__宏的判别,__CHECKER__宏在经过Sparse(Semantic Parser for C)东西对内核代码进行查看时会界说的。在运用make C=1或C=2时便会调用该东西,这个东西能够查看在代码中声明晰sparse所能查看到的相关特点的内核函数和变量。

  • 假如界说了__CHECKER__,__chk_user_ptr和__chk_io_ptr在这儿只声明函数,没有函数体,意图便是在编译过程中Sparse能够捕捉到编译过错,查看参数的类型。
  • 假如没有界说__CHECKER__,这便是一个空函数。

请看详细的界说(linux/compiler.h):

[plain]view plaincopy

print?

  1. #ifdef__CHECKER__
  2. externvoid__chk_user_ptr(void__user*);
  3. externvoid__chk_io_ptr(void__iomem*);
  4. #else
  5. #define__chk_user_ptr(x)(void)0
  6. #define__chk_io_ptr(x)(void)0
  7. #endif

(3)接下来是汇编:
adds %1, %2, %3
sum = addr + size 这个操作影响状况位(意图是影响是进位标志C),以下的两个指令都带有条件CC,也便是当C=0的时分才履行。

假如上面的加法指令进位了(C=1),则以下的指令都不履行,flag就为初始值current_thread_info()->addr_limit(非0),并回来。
假如没有进位(C=0),就履行下面的指令:
sbcccs %1, %1, %0
sum = sum – flag – 1,也便是(addr + size) – (current_thread_info()->addr_limit) – 1,操作影响符号位。
假如(addr + size) >= (current_thread_info()->addr_limit) – 1,则C=1
假如(addr + size) < (current_thread_info()->addr_limit) – 1,则C=0
当C=0的时分履行以下指令,不然越过(flag非零)。
movcc %0, #0
flag = 0,给flag赋值0。

综上所述:__range_ok宏其实等价于:

  • 假如(addr + size) >= (current_thread_info()->addr_limit) – 1,回来非零值
  • 假如(addr + size) < (current_thread_info()->addr_limit),回来零

而access_ok便是查验即将操作的用户空间的地址规模是否在当时进程的用户地址空间约束中。这个宏的功用很简单,彻底能够用C完成,不是有必要运用汇编。但于这两个函数运用频频,就运用汇编来完成部分功用来添加功率。
从这儿再次能够认识到,copy_from_user的运用是结合进程上下文的,由于他们要拜访“user”的内存空间,这个“user”有必要是某个特定的进程。经过上面的源码就知道,其间运用了current_thread_info()来查看空间是否能够拜访。假如在驱动中运用这两个函数,有必要是在完成体系调用的函数中运用,不可在完成中止处理的函数中运用。假如在中止上下文中运用了,那代码就很或许操作了底子不相关的进程地址空间。其次由于操作的页面或许被换出,这两个函数或许会休眠,所以相同不可在中止上下文中运用。

1.2.__arch_copy_from_user

在深化解说之前,咱们先想一个问题:为什么要运用copy_from_user函数???理论上,内核空间能够直接运用用户空间传过来的指针,即便要做数据复制的动作,也能够直接运用memcpy,事实上,在没有MMU的体系架构上,copy_form_user终究的完成便是利用了memcpy。但关于大多数有MMU的渠道,状况就有了一些改变:用户空间传过来的指针是在虚拟地址空间上的,它指向的虚拟地址空间很或许还没有真实映射到实践的物理页面上。但这又能怎样呢?缺页导致的反常会通明的被内核予以修正(为缺页的地址空间提交新的物理页面),拜访到缺页的指令会持续运转似乎什么都没有发生相同。但这仅仅用户空间缺页反常的行为,在内核空间这样却因一场有必要被显现的修正,这是由内核供给的缺页反常处理函数的规划形式决议的,其背面的思维后:在内核态中,假如程序企图拜访一个没有提交物理页面的用户空间地址,内核有必要对此保持警惕而不能像用户空间那样毫无发觉。
假如内核拜访一个没有被提交物理页面的空间,将发生缺页反常,内核会调用do_page_fault,由于反常发生在内核空间,do_page_fault将调用search_exception_tables在“ __ex_table”中查找反常指令的修正指令,在__arch_copy_from_user函数中常常运用USER宏,这个宏中了界说了“__ex_table”section。
linux/include/asm-arm/assembler.h
[plain]view plaincopy

print?

  1. #defineUSER(x…)\
  2. 9999:x;\
  3. .section__ex_table,”a”;\
  4. .align3;\
  5. .long9999b,9001f;\
  6. .previous
该界说中有如下数据;
[plain]view plaincopy

print?

  1. .long9999b,9001f;

其间9999b对应标号9999处的指令,9001f是9001处的指令,是9999b处指令的修正指令。这样,当标号9999处发生缺页反常时,体系将调用do_page_fault提交物理页面,然后跳到9001持续履行。

假如在驱动程序中不运用copy_from_user而用memcpy来替代,关于上述的景象会发生什么成果呢?当标号9999出发生缺页反常时,体系在“__ex_table”section总将找不到修正地址,由于memcpy没有像copy_from_user那样界说一个“__ex_table”section,此刻do_page_fault将经过no_context函数发生Oops。极有或许会看到相似如下信息:
Unable to handle kernel NULL pointer dereference at virtual address 00000fe0
一切为了保证设备驱动程序的安全,应该运用copy_from_user函数而不是memcpy。

下面咱们深化剖析__arch_copy_from_user函数的完成,该函数是用汇编完成的,界说在linux/arch/arm/lib/uaccess.S文件中。

[plain]view plaincopy

print?

  1. /*Prototype:unsignedlong__arch_copy_from_user(void*to,constvoid*from,unsignedlongn);
  2. *Purpose:copyablockfromusermemorytokernelmemory
  3. *Params:to-kernelmemory
  4. *:from-usermemory
  5. *:n-numberofbytestocopy
  6. *Returns:NumberofbytesNOTcopied.
  7. */
  8. .cfu_dest_not_aligned:
  9. rsbip,ip,#4
  10. cmpip,#2
  11. USER(ldrbtr3,[r1],#1)@Mayfault
  12. strbr3,[r0],#1
  13. USER(ldrgebtr3,[r1],#1)@Mayfault
  14. strgebr3,[r0],#1
  15. USER(ldrgtbtr3,[r1],#1)@Mayfault
  16. strgtbr3,[r0],#1
  17. subr2,r2,ip
  18. b.cfu_dest_aligned
  19. ENTRY(__arch_copy_from_user)
  20. stmfdsp!,{r0,r2,r4-r7,lr}
  21. cmpr2,#4
  22. blt.cfu_not_enough
  23. PLD(pld[r1,#0])
  24. PLD(pld[r0,#0])
  25. andsip,r0,#3
  26. bne.cfu_dest_not_aligned
  27. .cfu_dest_aligned:
  28. andsip,r1,#3
  29. bne.cfu_src_not_aligned
  30. /*
  31. *Seeingastherehastobeatleast8bytestocopy,wecan
  32. *copyoneword,andforceauser-modepagefault…
  33. */
  34. .cfu_0fupi:subsr2,r2,#4
  35. addmiip,r2,#4
  36. bmi.cfu_0nowords
  37. USER(ldrtr3,[r1],#4)
  38. strr3,[r0],#4
  39. movip,r1,lsl#32-PAGE_SHIFT@Oneachpage,useald/st??tinstruction
  40. rsbip,ip,#0
  41. movsip,ip,lsr#32-PAGE_SHIFT
  42. beq.cfu_0fupi
  43. /*
  44. *ip=maxno.ofbytestocopybeforeneedinganother”strt”insn
  45. */
  46. cmpr2,ip
  47. movltip,r2
  48. subr2,r2,ip
  49. subsip,ip,#32
  50. blt.cfu_0rem8lp
  51. PLD(pld[r1,#28])
  52. PLD(pld[r0,#28])
  53. PLD(subsip,ip,#64)
  54. PLD(blt.cfu_0cpynopld)
  55. PLD(pld[r1,#60])
  56. PLD(pld[r0,#60])
  57. .cfu_0cpy8lp:
  58. PLD(pld[r1,#92])
  59. PLD(pld[r0,#92])
  60. .cfu_0cpynopld:ldmiar1!,{r3-r6}@Shouldntfault
  61. stmiar0!,{r3-r6}
  62. ldmiar1!,{r3-r6}@Shouldntfault
  63. subsip,ip,#32
  64. stmiar0!,{r3-r6}
  65. bpl.cfu_0cpy8lp
  66. PLD(cmnip,#64)
  67. PLD(bge.cfu_0cpynopld)
  68. PLD(addip,ip,#64)
  69. .cfu_0rem8lp:cmnip,#16
  70. ldmgeiar1!,{r3-r6}@Shouldntfault
  71. stmgeiar0!,{r3-r6}
  72. tstip,#8
  73. ldmneiar1!,{r3-r4}@Shouldntfault
  74. stmneiar0!,{r3-r4}
  75. tstip,#4
  76. ldrnetr3,[r1],#4@Shouldntfault
  77. strner3,[r0],#4
  78. andsip,ip,#3
  79. beq.cfu_0fupi
  80. .cfu_0nowords:teqip,#0
  81. beq.cfu_finished
  82. .cfu_nowords:cmpip,#2
  83. USER(ldrbtr3,[r1],#1)@Mayfault
  84. strbr3,[r0],#1
  85. USER(ldrgebtr3,[r1],#1)@Mayfault
  86. strgebr3,[r0],#1
  87. USER(ldrgtbtr3,[r1],#1)@Mayfault
  88. strgtbr3,[r0],#1
  89. b.cfu_finished
  90. .cfu_not_enough:
  91. movsip,r2
  92. bne.cfu_nowords
  93. .cfu_finished:movr0,#0
  94. addsp,sp,#8
  95. LOADREGS(fd,sp!,{r4-r7,pc})
  96. .cfu_src_not_aligned:
  97. bicr1,r1,#3
  98. USER(ldrtr7,[r1],#4)@Mayfault
  99. cmpip,#2
  100. bgt.cfu_3fupi
  101. beq.cfu_2fupi
  102. .cfu_1fupi:subsr2,r2,#4
  103. addmiip,r2,#4
  104. bmi.cfu_1nowords
  105. movr3,r7,pull#8
  106. USER(ldrtr7,[r1],#4)@Mayfault
  107. orrr3,r3,r7,push#24
  108. strr3,[r0],#4
  109. movip,r1,lsl#32-PAGE_SHIFT
  110. rsbip,ip,#0
  111. movsip,ip,lsr#32-PAGE_SHIFT
  112. beq.cfu_1fupi
  113. cmpr2,ip
  114. movltip,r2
  115. subr2,r2,ip
  116. subsip,ip,#16
  117. blt.cfu_1rem8lp
  118. PLD(pld[r1,#12])
  119. PLD(pld[r0,#12])
  120. PLD(subsip,ip,#32)
  121. PLD(blt.cfu_1cpynopld)
  122. PLD(pld[r1,#28])
  123. PLD(pld[r0,#28])
  124. .cfu_1cpy8lp:
  125. PLD(pld[r1,#44])
  126. PLD(pld[r0,#44])
  127. .cfu_1cpynopld:movr3,r7,pull#8
  128. ldmiar1!,{r4-r7}@Shouldntfault
  129. subsip,ip,#16
  130. orrr3,r3,r4,push#24
  131. movr4,r4,pull#8
  132. orrr4,r4,r5,push#24
  133. movr5,r5,pull#8
  134. orrr5,r5,r6,push#24
  135. movr6,r6,pull#8
  136. orrr6,r6,r7,push#24
  137. stmiar0!,{r3-r6}
  138. bpl.cfu_1cpy8lp
  139. PLD(cmnip,#32)
  140. PLD(bge.cfu_1cpynopld)
  141. PLD(addip,ip,#32)
  142. .cfu_1rem8lp:tstip,#8
  143. movner3,r7,pull#8
  144. ldmneiar1!,{r4,r7}@Shouldntfault
  145. orrner3,r3,r4,push#24
  146. movner4,r4,pull#8
  147. orrner4,r4,r7,push#24
  148. stmneiar0!,{r3-r4}
  149. tstip,#4
  150. movner3,r7,pull#8
  151. USER(ldrnetr7,[r1],#4)@Mayfault
  152. orrner3,r3,r7,push#24
  153. strner3,[r0],#4
  154. andsip,ip,#3
  155. beq.cfu_1fupi
  156. .cfu_1nowords:movr3,r7,get_byte_1
  157. teqip,#0
  158. beq.cfu_finished
  159. cmpip,#2
  160. strbr3,[r0],#1
  161. movger3,r7,get_byte_2
  162. strgebr3,[r0],#1
  163. movgtr3,r7,get_byte_3
  164. strgtbr3,[r0],#1
  165. b.cfu_finished
  166. .cfu_2fupi:subsr2,r2,#4
  167. addmiip,r2,#4
  168. bmi.cfu_2nowords
  169. movr3,r7,pull#16
  170. USER(ldrtr7,[r1],#4)@Mayfault
  171. orrr3,r3,r7,push#16
  172. strr3,[r0],#4
  173. movip,r1,lsl#32-PAGE_SHIFT
  174. rsbip,ip,#0
  175. movsip,ip,lsr#32-PAGE_SHIFT
  176. beq.cfu_2fupi
  177. cmpr2,ip
  178. movltip,r2
  179. subr2,r2,ip
  180. subsip,ip,#16
  181. blt.cfu_2rem8lp
  182. PLD(pld[r1,#12])
  183. PLD(pld[r0,#12])
  184. PLD(subsip,ip,#32)
  185. PLD(blt.cfu_2cpynopld)
  186. PLD(pld[r1,#28])
  187. PLD(pld[r0,#28])
  188. .cfu_2cpy8lp:
  189. PLD(pld[r1,#44])
  190. PLD(pld[r0,#44])
  191. .cfu_2cpynopld:movr3,r7,pull#16
  192. ldmiar1!,{r4-r7}@Shouldntfault
  193. subsip,ip,#16
  194. orrr3,r3,r4,push#16
  195. movr4,r4,pull#16
  196. orrr4,r4,r5,push#16
  197. movr5,r5,pull#16
  198. orrr5,r5,r6,push#16
  199. movr6,r6,pull#16
  200. orrr6,r6,r7,push#16
  201. stmiar0!,{r3-r6}
  202. bpl.cfu_2cpy8lp
  203. PLD(cmnip,#32)
  204. PLD(bge.cfu_2cpynopld)
  205. PLD(addip,ip,#32)
  206. .cfu_2rem8lp:tstip,#8
  207. movner3,r7,pull#16
  208. ldmneiar1!,{r4,r7}@Shouldntfault
  209. orrner3,r3,r4,push#16
  210. movner4,r4,pull#16
  211. orrner4,r4,r7,push#16
  212. stmneiar0!,{r3-r4}
  213. tstip,#4
  214. movner3,r7,pull#16
  215. USER(ldrnetr7,[r1],#4)@Mayfault
  216. orrner3,r3,r7,push#16
  217. strner3,[r0],#4
  218. andsip,ip,#3
  219. beq.cfu_2fupi
  220. .cfu_2nowords:movr3,r7,get_byte_2
  221. teqip,#0
  222. beq.cfu_finished
  223. cmpip,#2
  224. strbr3,[r0],#1
  225. movger3,r7,get_byte_3
  226. strgebr3,[r0],#1
  227. USER(ldrgtbtr3,[r1],#0)@Mayfault
  228. strgtbr3,[r0],#1
  229. b.cfu_finished
  230. .cfu_3fupi:subsr2,r2,#4
  231. addmiip,r2,#4
  232. bmi.cfu_3nowords
  233. movr3,r7,pull#24
  234. USER(ldrtr7,[r1],#4)@Mayfault
  235. orrr3,r3,r7,push#8
  236. strr3,[r0],#4
  237. movip,r1,lsl#32-PAGE_SHIFT
  238. rsbip,ip,#0
  239. movsip,ip,lsr#32-PAGE_SHIFT
  240. beq.cfu_3fupi
  241. cmpr2,ip
  242. movltip,r2
  243. subr2,r2,ip
  244. subsip,ip,#16
  245. blt.cfu_3rem8lp
  246. PLD(pld[r1,#12])
  247. PLD(pld[r0,#12])
  248. PLD(subsip,ip,#32)
  249. PLD(blt.cfu_3cpynopld)
  250. PLD(pld[r1,#28])
  251. PLD(pld[r0,#28])
  252. .cfu_3cpy8lp:
  253. PLD(pld[r1,#44])
  254. PLD(pld[r0,#44])
  255. .cfu_3cpynopld:movr3,r7,pull#24
  256. ldmiar1!,{r4-r7}@Shouldntfault
  257. orrr3,r3,r4,push#8
  258. movr4,r4,pull#24
  259. orrr4,r4,r5,push#8
  260. movr5,r5,pull#24
  261. orrr5,r5,r6,push#8
  262. movr6,r6,pull#24
  263. orrr6,r6,r7,push#8
  264. stmiar0!,{r3-r6}
  265. subsip,ip,#16
  266. bpl.cfu_3cpy8lp
  267. PLD(cmnip,#32)
  268. PLD(bge.cfu_3cpynopld)
  269. PLD(addip,ip,#32)
  270. .cfu_3rem8lp:tstip,#8
  271. movner3,r7,pull#24
  272. ldmneiar1!,{r4,r7}@Shouldntfault
  273. orrner3,r3,r4,push#8
  274. movner4,r4,pull#24
  275. orrner4,r4,r7,push#8
  276. stmneiar0!,{r3-r4}
  277. tstip,#4
  278. movner3,r7,pull#24
  279. USER(ldrnetr7,[r1],#4)@Mayfault
  280. orrner3,r3,r7,push#8
  281. strner3,[r0],#4
  282. andsip,ip,#3
  283. beq.cfu_3fupi
  284. .cfu_3nowords:movr3,r7,get_byte_3
  285. teqip,#0
  286. beq.cfu_finished
  287. cmpip,#2
  288. strbr3,[r0],#1
  289. USER(ldrgebtr3,[r1],#1)@Mayfault
  290. strgebr3,[r0],#1
  291. USER(ldrgtbtr3,[r1],#1)@Mayfault
  292. strgtbr3,[r0],#1
  293. b.cfu_finished
  294. .section.fixup,”ax”
  295. .align0
  296. /*
  297. *Wetookanexception.r0containsapointerto
  298. *thebytenotcopied.
  299. */
  300. 9001:ldrr2,[sp],#4@void*to
  301. subr2,r0,r2@bytescopied
  302. ldrr1,[sp],#4@unsignedlongcount
  303. subsr4,r1,r2@byteslefttocopy
  304. movner1,r4
  305. blne__memzero
  306. movr0,r4
  307. LOADREGS(fd,sp!,{r4-r7,pc})
  308. .previous

咱们将在另一篇博文中详细剖析该函数。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/news/dongtai/262402.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部