Lorsque le CPU est en mode utilisateur, le CPU ne peut pas exécuter d'instructions privilégiées et ne peut pas accéder à la mémoire de l'espace du noyau.
Et lorsque le CPU est en mode noyau, le CPU peut exécuter toutes les instructions et accéder à toute la mémoire.
Maintenant, sous Linux, un programme en mode utilisateur peut accéder à toute la mémoire (en utilisant /dev/mem
) et peut exécuter les deux instructions privilégiées IN
et OUT
(en utilisant iopl()
je pense).
Donc, un programme en mode utilisateur sous Linux peut faire la plupart des choses (je pense que la plupart des choses) qui peuvent être faites en mode noyau.
Le fait de permettre à un programme en mode utilisateur d'avoir toute cette puissance ne va-t-il pas à l'encontre du but d'avoir des modes CPU?
iopl
ne permet pas toutes les instructions privilégiées, il est donc toujours utile de s'assurer qu'un programme de l'espace utilisateur bogué ne s'exécute pas accidentellementinvd
en sautant à travers un pointeur de fonction corrompu qui pointe vers la mémoire exécutable commençant par des0F 08
octets. J'ai ajouté une réponse avec certaines des raisons non liées à la sécurité pour lesquelles il est utile que les processus de l'espace utilisateur élèvent leurs privilèges.Ce n'est que de la même manière que
modprobe
"défait" la sécurité en chargeant du nouveau code dans le noyau.Pour diverses raisons, il est parfois plus logique de faire exécuter du code semi-privilégié (comme les pilotes graphiques à l'intérieur du serveur X) dans l'espace utilisateur plutôt que dans un thread du noyau.
kill
faire plus facilement, à moins qu'il ne bloque le matériel.Cela ne fait pas grand-chose pour la sécurité, mais il y a de gros avantages en termes de fiabilité et d'architecture logicielle.
La cuisson de pilotes graphiques dans le noyau peut réduire les changements de contexte entre les clients X et le serveur X, comme un seul utilisateur-> noyau-> utilisateur au lieu d'avoir à transférer des données dans un autre processus d'espace d'utilisation, mais les serveurs X sont historiquement trop gros et trop bogués pour les vouloir pleinement dans le noyau.
Oui, un code malveillant avec ces privilèges pourrait reprendre le noyau s'il le voulait, en utilisant
/dev/mem
pour modifier le code du noyau.Ou sur x86 par exemple, exécutez une
cli
instruction pour désactiver les interruptions sur ce cœur après avoir effectué uniopl
appel système pour définir son niveau de privilège d'E / S sur 0.Mais même x86
iopl
"seulement" donne accès à certaines instructions : in / out (et les versions de chaîne ins / outs), et cli / sti. Il ne vous permet pas d'utiliserrdmsr
ouwrmsr
de lire ou d'écrire des "registres spécifiques au modèle" (par exemple,IA32_LSTAR
qui définit l'adresse du point d'entrée du noyau pour l'syscall
instruction x86-64 ), nilidt
de remplacer la table des descripteurs d'interruption (qui vous permettrait de prendre totalement sur la machine à partir du noyau existant, au moins sur ce noyau.)Vous ne pouvez même pas lire les registres de contrôle (comme CR3 qui contient l'adresse physique du répertoire de pages de niveau supérieur, qu'un processus d'attaque pourrait trouver utile comme décalage
/dev/mem
pour modifier ses propres tables de pages comme alternative à enmmap
ingérer davantage)/dev/mem
. )invd
(invalider tous les caches sans réécriture !! ( cas d'utilisation = BIOS précoce avant que la RAM ne soit configurée)) est un autre amusant qui nécessite toujours un CPL 0 complet (niveau de privilège actuel), pas seulement IOPL. Mêmewbinvd
est privilégié car il est si lent (et non interruptible), et doit vider tous les caches sur tous les cœurs. (Voir Est - il possible de vider l'intégralité du cache du processeur lié à un programme? Et l' utilisation d'instructions WBINVD )Les bogues qui entraînent un saut vers une mauvaise adresse exécutant des données en tant que code ne peuvent donc exécuter aucune de ces instructions par accident dans un serveur X de l'espace utilisateur.
Le niveau de privilège actuel (en mode protégé et long) correspond aux 2 bits de poids faible
cs
(le sélecteur de segment de code) .mov eax, cs
/and eax, 3
fonctionne dans n'importe quel mode pour lire le niveau de privilège.Pour écrire le niveau de privilège, vous effectuez un
jmp far
oucall far
pour définirCS:RIP
(mais l'entrée GDT / LDT pour le segment cible peut le restreindre en fonction de l'ancien niveau de privilège, c'est pourquoi l'espace utilisateur ne peut pas faire cela pour s'élever). Ou vous utilisezint
ousyscall
pour basculer vers l'anneau 0 à un point d'entrée du noyau.la source