新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > linux內核中的copy_to_user和copy_from_user(一)

        linux內核中的copy_to_user和copy_from_user(一)

        作者: 時間:2016-11-22 來源:網絡 收藏
        Kernel version:2.6.14

        CPU architecture:ARM920T

        本文引用地址:http://www.104case.com/article/201611/319995.htm

        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. ...
        3. externvoid__chk_user_ptr(void__user*);
        4. externvoid__chk_io_ptr(void__iomem*);
        5. #else
        6. ...
        7. #define__chk_user_ptr(x)(void)0
        8. #define__chk_io_ptr(x)(void)0
        9. ...
        10. #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
        我們將在另一篇博文中詳細分析該函數。


        評論


        技術專區

        關閉
        主站蜘蛛池模板: 伊宁市| 张家口市| 寿宁县| 西乡县| 河池市| 奉化市| 易门县| 永春县| 永昌县| 尚义县| 定远县| 唐河县| 随州市| 蓬溪县| 林周县| 图木舒克市| 高要市| 钟山县| 松滋市| 龙岩市| 丰台区| 方正县| 门源| 琼海市| 海南省| 黄浦区| 稻城县| 龙泉市| 股票| 南部县| 徐汇区| 永川市| 白城市| 仲巴县| 三河市| 莆田市| 商水县| 泸溪县| 深圳市| 赫章县| 富蕴县|