2012-09-10 338 views
4

我一直在做一些研究,我對這個宏有點困惑。希望有人能給我一些指導。我有一些IOCTL代碼(我繼承,不能寫)和它如果檢查access_ok()上移動到了從用戶空間複製數據前的第一件事:什麼是使用Linux宏access_ok()

#define __lddk_copy_from_user(a,b,c) copy_from_user(a,b,c) 
#define __lddk_copy_to_user(a,b,c) copy_to_user(a,b,c) 

long can_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 
{ 
    switch(cmd) { 
    case COMMAND: 
     if(! access_ok(VERIFY_READ, (void *)arg, sizeof(Message_par_t))) 
     return(retval); 

     if(! access_ok(VERIFY_WRITE, (void *)arg, sizeof(Message_par_t))) 
     return(retval); 

     argp = &Command; 
     __lddk_copy_from_user((void *) argp,(Command_par_t *) arg, sizeof(Command_par_t)); 

因此,代碼工作得很好,但我不確定是否需要。第一個問題來自access_ok的返回描述:

  • 如果該區域可能可訪問(儘管訪問仍可能導致-EFAULT),該函數將返回非零值。這個函數簡單地檢查地址是否可能在用戶空間中,而不是在內核中。

因此,這意味着它確實沒有更多然後確保我們正在檢查對指針可能在用戶空間初始化?既然我們知道除了用戶空間調用之外我們不能進入這個功能,除非我們打開一個有效的文件描述符到這個設備,否則這是不可能發生的,這真的需要嗎?確實沒有得到一個NULL指針是否真的更安全?

第二個問題來自這樣的描述:

  • type參數可以被指定爲VERIFY_READ或VERIFY_WRITE。 VERIFY_WRITE符號還標識內存區域是否可讀和可寫。

這是否意味着我的代碼中的第一個檢查是多餘的?如果我們要檢查可寫區域,我們可以作爲免費贈品閱讀嗎?

我使用x86架構,因此access_ok()和__range_no_ok()的定義是從/usr/src/linux-3.1.10-1.16/arch/x86/include/asm/uaccess.h中定義的如下:

#define access_ok(type, addr, size) (likely(__range_not_ok(addr, size) == 0)) 

#define __range_not_ok(addr, size)     \ 
({         \ 
    unsigned long flag, roksum;     \ 
    __chk_user_ptr(addr);      \ 
    asm("add %3,%1 ; sbb %0,%0 ; cmp %1,%4 ; sbb $0,%0"  \ 
     : "=&r" (flag), "=r" (roksum)    \ 
     : "1" (addr), "g" ((long)(size)),    \ 
      "rm" (current_thread_info()->addr_limit.seg));  \ 
    flag;        \ 
}) 
+0

我不是Linux內核專家,但根據我的經驗,通常我認爲最好只嘗試一下操作,讓函數失敗,而不是事先執行這些檢查*仍然*必須爲失敗(除非有一些性能原因,爲什麼提前進行支票可能會節省大量時間);同樣,檢查它看起來像'copy_from_user'的源已經執行了讀取檢查,所以它是多餘的。只要檢查它的返回值(=不能被複制的字節數),如果它不是零則失敗,你必須返回一個錯誤。 –

+1

對此的回答取決於'__lddk_copy_from_user()'函數/宏的作用 - 這不在主流內核中。你能用這個函數的定義來更新你的問題嗎? – caf

+0

@caf - 好點,這是「標準的」uCLinux can驅動程序,並且該封裝器在can_defs.h中定義,但是,在我不知道它是什麼之前,我必須先查看它。 – Mike

回答

9

如果__lddk_copy_from_user()只是調用copy_from_user(),那麼access_ok()檢查是多餘的,因爲copy_from_user()自己執行這些檢查。

access_ok()檢查確保用戶空間應用程序不要求內核讀取或寫入內核地址(它們是完整性/安全檢查)。僅僅因爲用戶空間提供了一個指針並不意味着它肯定是一個用戶空間指針 - 在許多情況下,「內核指針」僅僅意味着它指向虛擬地址空間的特定區域。

此外,撥打access_ok()VERIFY_WRITE意味着VERIFY_READ,所以如果你檢查前者,你不需要再檢查後者。

1

access_ok宏只是對指針可能有效性的快速檢查。例如,它會用NULL或只讀參數捕獲錯誤的調用。

理想情況下,即使稍後訪問功能失敗,驅動程序也應恢復。然而,這隻能在昂貴的硬件操作之後纔會發生。因此,儘早檢查可能會使驅動程序對最常見的用戶空間程序員錯誤更加健壯。

編輯:是的,如果你唯一的輸入調用是copy_from_user()那裏,檢查是多餘的。但是,如果稍後寫入,在開始時檢查可寫性是很有用的。

你應該真的檢查copy_from_user()的返回值。

+0

你能向我解釋一下如何用一個NULL指針「捕捉」調用嗎?我看到'__range_not_ok()'調用'__chk_user_ptr()',所以它可能在那裏,但我找不到定義的位置。 – Mike

+1

我不相信這是真的,它只檢查它是否在x86的用戶空間地址範圍內。空指針在該範圍內,因此可以通過。 – jleahy

1

這不是多餘的。 access_ok()驗證地址,試圖從這個區域讀取。如果地址是有效的並且存在於用戶空間中,則它嘗試讀取,否則該函數返回EFAULT,其指示地址失敗。這是一種更安全的方式來驗證區域的訪問權限,而不是檢查NULL(在發生任何可能導致內核崩潰的分段錯誤之前)。

此外,您可以使用access_ok()驗證讀/寫訪問權限。第二個調用只是驗證該區域的「寫入」訪問。

+0

所以你的評論是我讀過的,也就是'VERIFY_WRITE符號也可以識別內存區域是否可讀和可寫.'是一個不準確的語句?因此即使access_ok(write)被調用,access_ok(read)仍然有用嗎? – Mike

+0

這是不準確的。你必須確定你是否擁有訪問內存區域的「權限」。你可能已經讀過權利或寫作,甚至可能都是這樣。 –