Le fait d'autoriser un programme en mode utilisateur à accéder à la mémoire de l'espace noyau et à exécuter les instructions IN et OUT ne va-t-il pas à l'encontre de l'objectif des modes CPU?

19

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 INet 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?

user341099
la source

Réponses:

23

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.

Eh bien, tous les programmes en mode utilisateur ne le peuvent pas, seulement ceux qui ont les privilèges appropriés. Et cela est déterminé par le noyau.

/dev/memest protégé par les autorisations d'accès au système de fichiers habituelles et la CAP_SYS_RAWIOcapacité. iopl()et ioperm()sont également limités par la même capacité.

/dev/mempeut également être compilé à partir du noyau ( CONFIG_DEVMEM).

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?

Eh bien, peut-être. Cela dépend de ce que vous voulez que les processus de l'espace utilisateur privilégié puissent faire. Les processus de l'espace utilisateur peuvent également mettre à la poubelle l'intégralité du disque dur s'ils ont accès à /dev/sda(ou équivalent), même si cela va à l'encontre de l'objectif d'avoir un pilote de système de fichiers pour gérer l'accès au stockage.

(Ensuite, il y a aussi le fait que cela iopl()fonctionne en utilisant les modes de privilège CPU sur i386, donc on ne peut pas dire que cela va à l'encontre de leur objectif.)

ilkkachu
la source
2
Même ioplne 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 accidentellement invden sautant à travers un pointeur de fonction corrompu qui pointe vers la mémoire exécutable commençant par des 0F 08octets. 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.
Peter Cordes
16

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.

  • Être capable de le killfaire plus facilement, à moins qu'il ne bloque le matériel.
  • Le faire demander à la page son code / données à partir de fichiers dans le système de fichiers. (La mémoire du noyau n'est pas paginable)
  • Lui donner son propre espace d'adressage virtuel où les bogues du serveur X peuvent simplement planter le serveur X, sans abattre le noyau.

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/mempour modifier le code du noyau.

Ou sur x86 par exemple, exécutez une cliinstruction pour désactiver les interruptions sur ce cœur après avoir effectué un ioplappel 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'utiliser rdmsrou wrmsrde lire ou d'écrire des "registres spécifiques au modèle" (par exemple, IA32_LSTARqui définit l'adresse du point d'entrée du noyau pour l' syscallinstruction x86-64 ), ni lidtde 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/mempour modifier ses propres tables de pages comme alternative à en mmapingé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ême wbinvdest 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, 3fonctionne dans n'importe quel mode pour lire le niveau de privilège.

Pour écrire le niveau de privilège, vous effectuez un jmp farou call farpour définir CS: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 utilisez intou syscallpour basculer vers l'anneau 0 à un point d'entrée du noyau.

Peter Cordes
la source
En fait, je suis à peu près certain que c'est juste un "sélecteur" de code dans Intel parlace. C'était un segment dans le 8086/8088, peut-être dans le 80186, mais par le 80286, il était appelé sélecteur, et je ne pense pas qu'ils aient officiellement changé cette terminologie depuis.
un CVn