Zircon? Qu'est ce que c'est
En août 2016, sans aucune annonce officielle de Google, les sources du nouveau système d'exploitation ont été découvertes
Fuchsia. Cet OS est basé sur un micro-noyau appelé Zircon, qui à son tour est basé sur LK (Little Kernel) .
Fuchsia n'est pas Linux
De quoi sera discuté dans l'article?
vDSO dans Zircon est le seul moyen d'accéder aux appels système (appels système) .
Est-il vraiment impossible d'appeler directement les instructions du processeur SYSENTER / SYSCALL à partir de notre code? Non, ces instructions de processeur ne font pas partie du système ABI. Le code utilisateur est interdit de suivre directement ces instructions.
Ceux qui veulent en savoir plus sur une telle étape architecturale, je vous invite à Cat.

Zircon vDSO (Virtual Dynamic Shared Object)
L'acronyme vDSO signifie V irtual D ynamic S hared O bject:
- Dynamic Shared Object est un terme utilisé pour faire référence aux bibliothèques partagées pour le format ELF (fichiers .so).
- Cet objet est virtuel car il ne se charge pas à partir d'un fichier séparé existant sur le système de fichiers. L'image vDSO est fournie directement par le noyau.
Prise en charge du noyau
La prise en charge de vDSO en tant que seul ABI contrôlé pour les applications en mode utilisateur est implémentée de deux manières:
Projection d'un objet mémoire virtuelle ( VMO, Virtual Memory Object ).
Lorsque zx_vmar_map traite VMO pour vDSO (et ZX_VM_PERM_EXECUTE
demandé dans les arguments), le noyau exige que le décalage et la taille correspondent strictement au segment exécutable vDSO. Cela (y compris) ne garantit qu'une seule projection vDSO dans la mémoire de processus. Après la première projection réussie de vDSO dans le processus, il ne peut plus être supprimé. Et une tentative de re-projeter vDSO dans la mémoire de processus, les tentatives de suppression du VMO projeté pour vDSO ou le projet avec le décalage et / ou la taille incorrects échouent avec l'erreur ZX_ERR_ACCESS_DENIED
.
Le décalage et la taille du code vDSO sont extraits au stade de la compilation à partir du fichier ELF puis utilisés dans le code du noyau pour effectuer les vérifications ci-dessus. Après la première projection vDSO réussie, le noyau du système d'exploitation se souvient de l'adresse du processus cible afin d'accélérer les vérifications.
Vérifiez les adresses de retour pour les fonctions d'appel système.
Lorsque le code en mode utilisateur appelle le noyau, un numéro d'appel système de bas niveau est transmis dans le registre. Les appels système de bas niveau sont l'interface interne (privée) entre vDSO et le noyau Zircon. Certains (la plupart) correspondent directement aux appels système de l'ABI public, d'autres non.
Pour chaque appel système de bas niveau dans le code vDSO, il existe un ensemble fixe de décalages dans le code qui effectuent cet appel. Le code source de vDSO définit des caractères internes qui identifient chacun de ces emplacements. Au moment de la compilation, ces emplacements sont extraits de la table des symboles vDSO et utilisés pour générer du code noyau qui détermine la prédiction de la validité de l'adresse de code pour chaque appel système de bas niveau. Ces prédicats vous permettent de vérifier rapidement la validité du code appelant, compte tenu du décalage par rapport au début du segment de code vDSO.
S'il est déterminé par le prédicat que le code appelant n'est pas autorisé à effectuer un appel système, une exception synthétique est levée, tout comme si le code appelant tentait d'exécuter une instruction inexistante ou privilégiée.
vDSO lors de la création d'un nouveau processus
Pour démarrer l'exécution du premier thread d'un processus nouvellement créé, l' appel système zx_process_start est utilisé . Le dernier paramètre de cet appel système (voir arg2 dans la documentation) transmet l'argument du premier thread du processus créé. Selon l'accord accepté, le chargeur de programme mappe vDSO à l'espace d'adressage du nouveau processus (à un endroit aléatoire sélectionné par le système) et transfère l'adresse de base du mappage avec l'argument arg2 au premier thread du processus créé. Cette adresse est l'adresse d'en-tête du fichier ELF, à laquelle les fonctions nommées nécessaires peuvent être trouvées pour effectuer des appels système.
Carte mémoire (mise en page) vDSO
vDSO est une bibliothèque partagée EFL régulière qui peut être considérée comme n'importe quelle autre. Mais pour vDSO, un petit sous-ensemble de l'ensemble du format ELF est intentionnellement sélectionné. Cela présente plusieurs avantages:
- Le mappage d'un tel ELF dans le processus est simple et n'inclut pas de cas limites complexes qui sont nécessaires pour soutenir pleinement les programmes ELF.
- L'utilisation de vDSO ne nécessite pas de liaison ELF dynamique entièrement fonctionnelle. En particulier, vDSO n'a pas de délocalisations dynamiques. La projection de segments PT_LOAD d'un fichier ELF est la seule action requise.
- Le code vDSO est sans état et réentrant. Il fonctionne exclusivement avec les registres du processeur et la pile. Cela le rend approprié pour une utilisation dans une grande variété de contextes avec des restrictions minimales, ce qui est conforme au système d'exploitation ABI obligatoire. Il simplifie également l'analyse et la vérification du code pour la fiabilité et la sécurité.
Toute la mémoire vDSO est représentée par deux segments consécutifs, chacun contenant des pages entières alignées:
- Le premier segment est en lecture seule et comprend des en-têtes ELF ainsi que des données constantes.
- Le deuxième segment est exécutable et contient du code vDSO.
L'image vDSO entière se compose uniquement des pages de ces deux segments. Seules deux valeurs extraites des en-têtes ELF sont requises pour afficher la mémoire vDSO: le nombre de pages dans chaque segment.
Données de constante de temps de démarrage du système d'exploitation
Certains appels système renvoient simplement des valeurs constantes (les valeurs doivent être demandées au moment de l'exécution et ne peuvent pas être compilées en code en mode utilisateur). Ces valeurs sont soit fixées dans le noyau au moment de la compilation, soit déterminées par le noyau au démarrage (paramètres de démarrage et paramètres matériels). Par exemple: zx_system_get_version () , zx_system_get_num_cpus () et zx_ticks_per_second () . La valeur de retour de la dernière fonction, par exemple, est affectée par le paramètre de ligne de commande du noyau .
Attendez, le nombre de CPU est-il constant?Fait intéressant, la description de la fonction zx_system_get_num_cpus () indique également explicitement que le système d'exploitation ne prend pas en charge le remplacement à chaud du nombre de processeurs:
Ce nombre ne peut pas changer pendant une exécution du système, uniquement au démarrage.
Cela, au moins, indique indirectement que le système d'exploitation n'est pas positionné en tant que serveur.
Étant donné que ces valeurs sont constantes, cela n'a aucun sens de payer pour des appels système réels vers le noyau du système d'exploitation. Au lieu de cela, leur implémentation est de simples fonctions C ++ qui renvoient des données lues à partir du segment constant vDSO. Les valeurs capturées lors de la compilation (telles que la chaîne de version du système) sont simplement compilées dans vDSO.
Pour les valeurs déterminées au démarrage, le noyau doit modifier le contenu de vDSO. Cela se fait à l'aide d'un code exécutable au début qui forme le VMO vDSO avant que le noyau ne démarre le premier processus utilisateur (et lui transmette le descripteur VMO). Lors de la compilation, les décalages de l'image vDSO ( vdso_constants ) sont extraits du fichier ELF puis intégrés au noyau. Et au démarrage, le noyau affiche temporairement des pages couvrant vdso_constants dans son propre espace d'adressage pour pré-initialiser la structure avec les valeurs correctes (pour le démarrage actuel du système).
Pourquoi tout ce mal de tête ?
L'une des raisons les plus importantes est la sécurité. Autrement dit, si un attaquant parvient à exécuter du code (shell-) arbitraire, il devra utiliser des fonctions vDSO pour appeler des fonctions système. Le premier obstacle sera la randomisation susmentionnée de l'adresse de démarrage vDSO pour chaque processus créé. Et puisque le noyau du système d'exploitation est responsable du VMO (objet de mémoire virtuelle) de vDSO, il peut choisir de mapper un vDSO complètement différent à un processus spécifique, interdisant ainsi les appels système dangereux (et non nécessaires pour un processus particulier). Par exemple: vous pouvez empêcher les pilotes de générer des processus enfants ou gérer la projection de zones MMIO. C'est un excellent outil pour réduire la surface d'attaque.
Remarque: Actuellement, la prise en charge de plusieurs vDSO est activement développée. Il existe déjà une implémentation de preuve de concept et des tests simples, mais davantage de travail est nécessaire pour améliorer la fiabilité de l'implémentation et déterminer les options disponibles. Le concept actuel fournit des options d'image vDSO qui n'exportent qu'un sous-ensemble de l'interface d'appel système vDSO complète.
Qu'en est-il des autres systèmes d'exploitation?Il convient de noter que des techniques similaires ont déjà été utilisées avec succès dans d'autres systèmes d'exploitation. Par exemple, sous Windows, il existe un ProcessSystemCallDisablePolicy :
Restriction de désactivation des appels système Win32k pour restreindre la possibilité d'utiliser NTUser et GDI