2017-08-01 135 views
6

發送由於IOKit命令我使用由於IOKit框架與使用從用戶空間客戶IOConnectCallMethodIOExternalMethodDispatch駕駛員側我的驅動程序通信。具有動態長度

到目前爲止我能夠發送固定長度指令,現在我想發送字符的不同大小的陣列(即FULLPATH)。

然而,似乎是,驅動器和所述的客戶端側命令長度耦合,這意味着checkStructureInputSizeIOExternalMethodDispatch在駕駛員必須在客戶端等於inputStructCntIOConnectCallMethod

這裏有結構內容兩側:

DRIVER:

struct IOExternalMethodDispatch 
{ 
    IOExternalMethodAction function; 
    uint32_t   checkScalarInputCount; 
    uint32_t   checkStructureInputSize; 
    uint32_t   checkScalarOutputCount; 
    uint32_t   checkStructureOutputSize; 
}; 

客戶:

kern_return_t IOConnectCallMethod(
    mach_port_t connection,  // In 
    uint32_t  selector,  // In 
    const uint64_t *input,   // In 
    uint32_t  inputCnt,  // In 
    const void  *inputStruct,  // In 
    size_t  inputStructCnt, // In 
    uint64_t *output,  // Out 
    uint32_t *outputCnt,  // In/Out 
    void  *outputStruct,  // Out 
    size_t  *outputStructCnt) // In/Out 

這裏是我的失敗嘗試使用不同大小的命令:

std::vector<char> rawData; //vector of chars 

// filling the vector with filePath ... 

kr = IOConnectCallMethod(_connection, kCommandIndex , 0, 0, rawData.data(), rawData.size(), 0, 0, 0, 0); 

而從駕駛員命令處理程序方面,我打電話IOUserClient::ExternalMethodIOExternalMethodArguments *argumentsIOExternalMethodDispatch *dispatch但是這需要我從客戶端,它是動態的傳遞數據的精確長度。

這不起作用,除非我應該期待數據的準確長度設置的調度功能。

任何想法如何解決這個或許有不同的API,我應該在這種情況下使用?

回答

2

正如您已經發現的那樣,接受可變長度「結構」輸入和輸出的答案是在IOExternalMethodDispatch中爲輸入或輸出結構大小指定特殊的kIOUCVariableStructureSize值。

這將使方法調度成功,招呼一下,你的方法實現。然而令人討厭的是,結構輸入和輸出不一定通過IOExternalMethodArguments結構中的structureInputstructureOutput指針字段來提供。在結構體定義(IOKit/IOUserClient。h)中,通知: - 交叉點而言通常是8192個字節2,或

struct IOExternalMethodArguments 
{ 
    … 

    const void * structureInput; 
    uint32_t  structureInputSize; 

    IOMemoryDescriptor * structureInputDescriptor; 

    … 

    void *  structureOutput; 
    uint32_t  structureOutputSize; 

    IOMemoryDescriptor * structureOutputDescriptor; 

    … 
}; 

根據實際大小,存儲區可能被structureInputstructureInputDescriptor(和structureOutputstructureOutputDescriptor)引用內存頁面。任何更小的內容都將作爲指針出現,任何更大的內容都將被內存描述符引用。不要指望具體的交叉點,這是一個實現細節,原則上可以改變。

您如何處理這種情況取決於您需要如何處理輸入或輸出數據。通常情況下,你需要直接在你的kext中讀取它 - 所以如果它作爲一個內存描述符進來,你需要首先將它映射到內核任務的地址空間。事情是這樣的:

static IOReturn my_external_method_impl(OSObject* target, void* reference, IOExternalMethodArguments* arguments) 
{ 
    IOMemoryMap* map = nullptr; 
    const void* input; 
    size_t input_size; 
    if (arguments->structureInputDescriptor != nullptr) 
    { 
     map = arguments->structureInputDescriptor->createMappingInTask(kernel_task, 0, kIOMapAnywhere | kIOMapReadOnly); 
     if (map == nullptr) 
     { 
      // insert error handling here 
      return …; 
     } 
     input = reinterpret_cast<const void*>(map->getAddress()); 
     input_size = map->getLength(); 
    } 
    else 
    { 
     input = arguments->structureInput; 
     input_size = arguments->structureInputSize; 
    } 

    // … 
    // do stuff with input here 
    // … 

    OSSafeReleaseNULL(map); // make sure we unmap on all function return paths! 
    return …; 
} 

輸出描述符同樣可以治療的,只是沒有當然的kIOMapReadOnly選擇!

注意:隱蔽安全風險:

解釋內核中的用戶數據通常是一個安全敏感的任務。直到最近,結構輸入機制特別脆弱 - 因爲輸入結構是從用戶空間到內核空間的內存映射,另一個用戶空間線程仍然可以在內核正在讀取它時修改該內存。您需要非常小心地編寫內核代碼,以避免向惡意用戶客戶端引入漏洞。例如,邊界檢查映射內存中的用戶空間提供的值,然後在假定它仍在有效範圍內的情況下重新讀取它是錯誤的。

避免這種情況最直接的方法是複製一次內存,然後僅使用複製的數據版本。要採取這種方法,您甚至不需要記憶描述符:您可以使用readBytes()成員函數。對於大量數據,儘管您可能不希望這樣做。

最近(在10.12.x週期中)Apple更改了structureInputDescriptor,因此它使用kIOMemoryMapCopyOnWrite選項創建。 (據我所知,這是專門爲此目的而創建的。)這樣做的結果是,如果用戶空間修改了內存範圍,它不會修改內核映射,但會透明地創建它寫入的頁面的副本。依靠這個假設你的用戶系統已經完全修補了。 即使在完全打補丁的系統上,structureOutputDescriptor也會遇到同樣的問題,所以從內核的角度來看它只能寫入。 永遠不要讀回你在那裏寫的任何數據。(寫時複製映射對輸出結構沒有意義。)

1

通過相關的手冊會再次後,我找到了相關段落:

的checkScalarInputCount,checkStructureInputSize,checkScalarOutputCount和checkStructureOutputSize領域允許參數列表的健全檢查其傳遞到前目標對象。標量計數應該設置爲目標方法期望讀取或寫入的標量(64位)值的數量。結構大小應該設置爲目標方法期望讀取或寫入的任何結構的大小。對於任何一個結構體大小字段,如果在編譯時無法確定結構體的大小,請指定kIOUCVariableStructureSize而不是實際大小。

所以我不得不以避免大小驗證做的,就是設置現場checkStructureInputSize重視在IoExternalMethodDispatchkIOUCVariableStructureSize並傳遞到驅動程序正確的命令。

+1

是的,這將做到這一點。請注意,如果「結構」超出了特定的大小(如果我沒有記錯的話,2頁/ 8K),您將在內核中將其作爲指針接收,而不是作爲內存描述符。 'IOExternalMethodArguments'中的'structureInput'字段將是一個'nullptr',你需要看一下'structInputDescriptor'字段。讓我知道你是否希望我在正確的答案中充實確切的程序。 – pmdj

+0

@pmdj,是的,代碼示例的正確答案確實值得歡迎。謝謝 – Zohar81

+0

完成!我已經深入瞭解更多細節,特別是安全隱患。 – pmdj