Canal caché dans Xen
Par Mickaël le 23 juillet 2008, 18h23 - Projets - Lien permanent
Il existe plusieurs moyens de virtualiser des ordinateurs. Xen le fait d'une certaine façon, avec comme base un hyperviseur qui se trouve avant les OS lancés. Il a tous les droits sur le matériel et peut contrôler les actions critiques des OS invités. Xen intègre une politique de sécurité qui permet de contrôler, suivant les besoins, les possibilités accordées aux OS virtualisés (en domU). Il existe un mécanisme de communication entre invités appelé XenStore qui permet de partager des plages mémoire en lecture/écriture. On peut alors échanger divers informations entre invités suivant les droits qui leurs sont accordés. Cependant il existe un moyen (au moins) d'échanger des données entre invités quelque soit la politique de Xen.
Fonctionnement
Ce canal caché est une preuve de concept pour communiquer entre invités (indépendamment du domaine : dom0 ou domU). Pour cela, on doit avoir les droits qui permettent d'insérer un module noyau, autrement dit de modifier le noyau, ce qui servira à faire des appels aux hypercalls.
On utilise la table de mapping entre les adresses physiques et les adresses pseudo-physique de l'hyperviseur (MFN->PFN : Machine Frame Number to Pseudo-physical Frame Number). Cette table permet d'avoir de très bonnes performances quand l'OS invité à besoin de zones contigües de mémoire. On peut ainsi avoir une mémoire physique moins fragmentée. Il utilise alors un hypercall (mmu_update) pour établir cette relation. On peut noter que cette table est lisible par tous les invités étant donné qu'il n'y en a qu'une seule. Évidement, seul le détenteur d'une plage d'adresse (fixée au démarrage et changeable avec l'utilisation du baloon driver) peut la modifier. On parle ici seulement d'adresses, mais en aucun cas des données vers lesquelles elles pointent. En effet, si on veut pouvoir lire à un emplacement, il faut pouvoir le mapper en mémoire. Ici on ne peut mapper que nos propres données. Cette table de relation contient normalement des adresses, mais étant donné qu'il n'y a aucune vérification, on peut écrire ce que l'on souhaite.
Le principe du canal caché est simple : il faut écrire un tag pour (notamment) pouvoir trouver l'emplacement, et ensuite inscrire les données que l'on veut passer à un autre invité. Une fois ces informations écrites, n'importe quel invité qui connais le tag peut retrouver et extraire les informations. On obtient ainsi un canal half-duplex dès que deux invités connaissent mutuellement leurs tags. Ce nouveau canal de communication fonctionne actuellement pour Linux 2.6 x86_32.
Cette preuve de concept n'est pas une idée nouvelle puisqu'elle à été relevée en 2006 sur la mailing-list de xen-devel. Au final, on outrepasse la politique de sécurité de Xen comme le disaient les développeurs.
La table machine-to-physical est une bonne idée pour augmenter les performances de mapping de mémoire, mais il serait judicieux d'avoir une table pour chaque invité. Elles auraient alors la même protection que la mémoire ordinaire et seraient inaccessible à un autre invité. À la vue de la table, je ne pense pas qu'il y ai de gros problèmes de performance par rapport à la vitesse de changement de table (effectué par Xen) et l'espace mémoire que prend une table. Une autre solution consiste à utiliser les shadow page tables, mais avec en contrepartie une baisse de performance si la translation des adresses est logiciel. Si cette remarque est appliquée, mon bout de code sera alors inutilisable. Cette méthode pour « isoler » les invités pourrais également servir à les empêcher de lire la table et d'en déduire (avec les interruptions Xen) une cartographie basique de la mémoire physiquement utilisée.
Mise en place
Le code du PoC est un pilote de périphérique qui crée /dev/xencc. Le protocole de communication est basé sur des tags qui contiennent un identifiant et la taille du message. On a besoin d'un tag différent pour chaque OS (voir les sources et le changer pour le second invité). Pour l'instant, on peut seulement communiquer entre 2 invités.
Si le système n'a pas udev d'installé le périphérique ne se créera pas automatiquement et il faudra donc le créer à la main[1] après l'insertion du module[2]. Une fois celui-ci chargé, on peut alors écrire[3] et lire[4]. Les plus aventureux pourront se risquer à augmenter la taille du tampon dans les sources. Le débit du canal de communication semble être bon étant donné qu'on utilise un hypercall (mmu_update) qui copie une plage complète de données (normalement des adresses) en un seul appel. L'inconvénient de cette méthode est la forte consommation de mémoire par rapport à la taille du message. En effet, on alloue une plage mémoire où on n'utilise que les « adresses » et non les données ainsi réservées. Il faut alors transférer les données petit à petit. Avec un bon tempo de synchronisation, on obtient un canal très rapide.
Exemple
Voilà les sorties générées lors de l'écriture de l'invité 1 et ensuite la lecture de l'invité 2 (avec un noyau 2.6.18-6-xen-686) :
invité 1 :
dom1:~# echo msg dom1 > /dev/xencc dom1:~# dmesg -c xencc loaded guest 1 xencc open xencc write to guest 2 xencc clean xencc order: 4 xencc alloc_pages: c1357400 xencc page_to_pfn: 000076a0 xencc pfn_to_mfn: 0000964c xencc allocate_mfn : 0000964c xencc pfn0: 00000000 -> 00025063 xencc pfn1: 00000000 -> 00003d19 xencc pfn2: 00000000 -> 00000588 xencc pfn3: 00000000 -> 00072a3c xencc pfn4: 00000000 -> 00000009 xencc pfn5: 00000000 -> 2067736d xencc pfn6: 00000000 -> 316d6f64 xencc pfn7: 00000000 -> 0000000a xencc nb_success: 8 xencc write xencc release
invité 2 :
dom2:~# dd if=/dev/xencc count=1 msg dom1 0+1 records in 0+1 records out 9 bytes (9 B) copied, 0.000185 s, 48.6 kB/s dom2:~# dmesg -c xencc loaded guest 2 xencc open xencc read from guest 1 xencc mfn 0000964c : 00025063 (0) xencc mfn 0000964d : 00003d19 (1) xencc mfn 0000964e : 00000588 (2) xencc mfn 0000964f : 00072a3c (3) xencc tag trouvé ! xencc release
La version actuelle est instable, à utiliser avec prudence !
Fichiers source.
Post dans la mailing-list de développement de Xen.