2012-08-26 26 views
1

涉及的結構如下所示。 結構節點int a; int b; }copy_from_user - 難以從用戶空間複製雙指針

struct ioctl_node { 
    struct node **ptr; 
    int   count; //Stores the no of pointers this ptr is pointing to. 
}; 

在用戶空間中,我已填充ioctl_node的結構與count = 3。 和ptr[0]ptr[2]指向類型節點的兩個結構,而ptr[1]NULL。我想把它傳遞給內核。 我已經發布了ioctl調用,將這個信息從用戶空間傳遞到內核空間。

我在內核空間中做了以下操作。

struct ioctl_node k_node; 

copy_from_user(&k_node, arg, sizeof(struct ioctl_node)) //arg is the source 

//k_node.count is showing the right value what I have set in the user space. 

struct node *tmp = NULL; 

然後我做到了,copy_from_user(&tmp, k_node.ptr, sizeof(struct node *),而且這次調用也成功返回。 但是,我有困難,在內核空間中正確複製**ptr的全部內容。 任何人都可以幫忙,我怎麼能做到這一點。我應該怎麼做下一個copy_from_user來複制所有的內容。我嘗試過,但它給複製錯誤。

+1

難道你不能改變你的規格來複制連續的內存區嗎?如果內部指針錯誤,你會怎麼做? –

回答

1

copy_from_user從用戶空間向內核空間複製一個連續字節塊。您需要先複製struct ioctl_node,然後將指針複製到單個節點,然後將struct node複製到一個循環中。一定要分配適量的內存。由於您事先知道節點數量,因此可以爲它們分配一個數組。

struct node_data { 
    size_t node_count; 
    struct nodes *nodes; 
}; 
struct node_data *read_nodes(struct ioctl_node_user_pointer *arg) 
{ 
    struct node_data *nd = NULL; 
    struct ioctl_node kin = {0}; 
    struct node *node_user_pointers = NULL; 
    int i; 
    int err; 
    /* Step 1: retrieve the root node */ 
    nd = kmalloc(sizeof(*nd), GFP_KERNEL); 
    if (!nd) { 
     err = -ENOMEM; 
     goto error; 
    } 
    if (copy_from_user(ioctl_node_user_pointer, &kin, sizeof(kin))) { 
     err = -EFAULT; 
     goto error; 
    } 
    if (kin->count < 0 || kin->count > ((size_t)-1)/sizeof(*nodes)) { 
     err = -EINVAL; 
     goto error; 
    } 
    nd->node_count = kin.count; 
    /* Step 2: retrieve the pointers to individual nodes */ 
    node_user_pointers = kmalloc(sizeof(*node_user_pointers) * nd->node_count, GFP_KERNEL); 
    if (node_user_pointers) { 
     err = -ENOMEM; 
     goto error; 
    } 
    if (copy_from_user(kin->nodes, node_user_pointers, sizeof(*node_user_pointers) * nd->node_count)) { 
     err = -EFAULT; 
     goto error; 
    } 
    /* Step 3: retrieve the nodes themselves */ 
    nd->nodes = kmalloc(sizeof(*nodes) * nd->node_count, GFP_KERNEL); 
    if (!nd->nodes) { 
     err = -ENOMEM; 
     goto error; 
    } 
    for (i = 0; i < head.count; i++) { 
     if (copy_from_user(node_user_pointers[i], nd->nodes + i, sizeof(nd->nodes[0]))) { 
      err = -EFAULT; 
      goto error; 
     } 
    } 
    kfree(node_user_pointers); 
    return nd; 
error: 
    if (nd) { 
     kfree(nd->nodes); 
     kfree(nd); 
    } 
    kfree(node_user_pointers); 
    return ERR_PTR(err); 
} 
+0

這看起來不夠:第一個'copy_from_user'是一個「淺拷貝」,所以'kin.ptr'仍然是一個指向用戶空間內存的指針。因此,'kin.ptr [i]'無法完成 - 您必須分配一個指針數組並複製它們,然後才能開始複製單個節點。或者關注[@BasileStarymlevitch](http://stackoverflow.com/users/841108/basile-starynkevitch)的建議並更改API以避免雙重間接。 – ephemient

+0

@ephemient Ooops,是的,謝謝。固定。 – Gilles

+0

我認爲你的意思是'返回-ENOMEM' ;-)之前的'if(!node_user_pointers)''linux/err.h'中有一個'ERR_PTR'助手來返回指針錯誤。無條件'kfree(node_user_pointers)'是安全的,因爲'kfree(NULL)'是一個無操作。另外,雖然這對於一個簡單示例的錯誤檢查來說有點太過分了,但驗證'sizeof()* count'不會溢出幷包裝是很好的,否則這將成爲用戶空間在未分配內核上塗寫的簡單方法記憶。 – ephemient