Interaction de la mémoire de la fourche et de l'espace utilisateur mis en correspondance dans le noyau

StackOverflow https://stackoverflow.com/questions/4046192

Question

Considérons un pilote Linux qui utilise get_user_pages (ou get_page) pour cartographier les pages du processus d'appel. L'adresse physique des pages sont ensuite transmises à un périphérique matériel. Le processus et l'appareil peut lire et écrire sur les pages jusqu'à ce que les parties décident de mettre fin à la communication. En particulier, la communication peut continuer à utiliser les pages après l'appel système qui appelle retourne get_user_pages. L'appel système est en effet mise en place d'un zone de mémoire partagée entre le procédé et le dispositif matériel .

Je suis préoccupé par ce qui se passe si les appels de processus fork (il pourrait être d'un autre fil, et pourrait se produire soit pendant la syscall que les appels get_user_pages est en cours ou plus tard). En particulier, si le parent écrit dans la zone de mémoire partagée après la fourche, qu'est-ce que je sais sur l'adresse physique sous-jacente (probablement changé en raison de copy-on-write)? Je veux comprendre:

  1. quels sont les besoins du noyau à faire pour se défendre contre un processus potentiellement mauvaise conduite (je ne veux pas créer une sécurité trou!);
  2. quelles restrictions la nécessité de processus d'obéir afin que la fonctionnalités de notre pilote fonctionne correctement (à savoir les restes de mémoire physique mis en correspondance à la même adresse dans le processus parent).

    • Idéalement, je voudrais le cas où le processus commun d'enfants n'utilise pas notre chauffeur du tout (il appelle probablement exec presque immédiatement) au travail.
    • Idéalement, le processus parent ne devrait pas avoir à prendre des mesures spéciales lors de l'allocation de la mémoire, comme nous l'avons code existant qui passe à une mémoire tampon allouée pile au conducteur.
    • Je suis au courant de madvise avec MADV_DONTFORK, et il serait autorisé à avoir la mémoire disparaît de l'espace du processus enfant, mais ce n'est pas applicable à une mémoire tampon allouée pile.
    • « Ne pas utiliser la fourche pendant que vous disposez d'une connexion active avec notre chauffeur » serait ennuyeux, mais acceptable en dernier ressort si le point 1 est satisfait.

Je suis prêt à être signalé à la documentation ou le code source. Je l'ai regardé en particulier Device Drivers Linux , mais n'a pas trouvé cette question abordée. RTFS appliquée à même que la partie pertinente de la source du noyau est un peu écrasante.

La version du noyau est pas complètement fixe mais est récente (disons ≥2.6.26). Nous ne ciblons des plates-formes (bras monoprocesseur jusqu'à présent, mais multicoeur est juste au coin), si elle compte.

Était-ce utile?

La solution

A fork() ne perturbera pas get_user_pages(). get_user_pages() vous donnera un struct page

Vous auriez besoin de kmap() avant de pouvoir y accéder, et ce mapping est fait dans l'espace du noyau, et non dans l'espace utilisateur.

EDIT:. get_user_pages() toucher la table de page, mais vous ne devriez pas être inquiet à ce sujet (il suffit de faire en sorte que les pages sont mises en correspondance dans l'espace utilisateur), et retourne -EFAULT si elle avait un problème faisant

Si vous fork (), jusqu'à ce que la copie sur écriture est effectuée, l'enfant sera en mesure de voir cette page. Une fois la copie sur écriture est fait (parce que l'enfant / le conducteur / le parent a écrit à la page grâce à la cartographie de l'espace utilisateur - pas le kmap du noyau () le pilote a), cette page ne sera plus partagée. Si vous détenez toujours un kmap () sur la page (dans le code du pilote), vous ne serez pas en mesure de savoir si vous tenez la page parent ou de l'enfant.

1) Ce n'est pas un trou de sécurité, car une fois que vous exec (), tout cela a disparu.

2) Lorsque vous fork () que vous voulez à la fois processus soit identique (C'est une fourchette !!). Je pense que votre conception doit permettre à la fois le parent et l'enfant pour accéder au pilote. Exec () va tout vider.

Qu'en est-il d'ajouter certaines fonctionnalités dans l'espace utilisateur comme:

 f = open("/dev/your_thing")
 mapping = mmap(f, ...)

Quand mmap () est appelée sur votre appareil, vous installez une carte mémoire, avec des drapeaux spéciaux: http://os1a.cs.columbia.edu/lxr /source/include/linux/mm.h#071

Vous avez des choses intéressantes comme:

#define VM_SHARED       0x00000008
#define VM_LOCKED       0x00002000
#define VM_DONTCOPY     0x00020000      /* Do not copy this vma on fork */

VM_SHARED désactivera copie sur écriture VM_LOCKED désactivera swapping sur cette page VM_DONTCOPY indiquera au noyau de ne pas copier la région vma sur la fourche, bien que je ne pense pas que ce soit une bonne idée

Autres conseils

La réponse courte est d'utiliser madvise(addr, len, MADV_DONTFORK) sur les tampons de l'espace utilisateur que vous donnez à votre pilote. Cela indique au noyau que le mappage ne doit pas être copié du parent à l'enfant et donc il n'y a pas CoW.

L'inconvénient est que l'enfant hérite pas de mappage à cette adresse, donc si vous voulez que l'enfant puis commencer à utiliser le pilote, il devra reprogrammer cette mémoire. Mais cela est assez facile à faire dans l'espace utilisateur.

Mise à jour :. Un tampon sur la pile est problématique, je ne suis pas sûr que vous pouvez le faire en toute sécurité en général

Vous ne pouvez pas marquer DONTFORK, parce que votre enfant pourrait être en cours d'exécution sur cette page de la pile quand il bifurque, ou (pire manière), il pourrait faire plus tard un retour de la fonction et appuyez sur la page de pile non cartographiées. (J'ai même testé, vous pouvez marquer votre bonheur DONTFORK de la pile, les mauvaises choses se produisent lorsque vous fourche).

L'autre façon d'éviter une vache est de créer une cartographie partagée, mais vous ne pouvez pas mapper votre pile partagée pour des raisons évidentes.

Cela signifie que vous risquez si vous une vache fourchette. Même si l'enfant « juste » execs il pourrait encore toucher la page de la pile et provoquer une vache, ce qui conduit au parent d'obtenir une autre page, ce qui est mauvais.

Le point d'un mineur en votre faveur est que le code en utilisant un seul tampon sur pile doit vous inquiétez pas sur le code qu'il appelle bifurquer, par exemple. vous ne pouvez pas utiliser une fois que la fonction est de retour tampon sur pile. Donc, il vous suffit de vérifier vos fonctions appelées, et si vous ne la fourche sont en sécurité, mais qui peuvent encore être infaisable, et est fragile si le code ne change jamais.

Je pense que vous voulez vraiment avoir toute la mémoire qui est donnée à votre pilote à venir d'un allocateur personnalisé dans l'espace utilisateur. Il ne devrait pas être intrusif. L'allocateur peut soit mmap votre appareil directement, comme l'autre réponse proposée, ou tout simplement utiliser mmap anonyme, madvise(DONTFORK), et probablement d'éviter mlock() échange sur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top