Ce qui se passe dans les deux cas est le même :pour exécuter un fichier directement, le bit d'exécution doit être défini et le système de fichiers ne peut pas être monté noexec. Mais ces choses n'empêchent rien de lire ces fichiers.
Lorsque le script bash est exécuté en tant que ./hello_world
et le fichier n'est pas exécutable (soit pas de bit d'autorisation exec, soit noexec sur le système de fichiers), le #!
la ligne n'est même pas vérifiée , car le système ne charge même pas le fichier. Le script n'est jamais "exécuté" au sens propre du terme.
Dans le cas de bash ./hello_world
, eh bien, l'option de système de fichiers noexec n'est tout simplement pas aussi intelligente que vous le souhaiteriez. Le bash
la commande exécutée est /bin/bash
, et /bin
n'est pas sur un système de fichiers avec noexec
. Donc, ça ne pose aucun problème. Le système ne se soucie pas que bash (ou python ou perl ou autre) soit un interpréteur. Il exécute simplement la commande que vous avez donnée (/bin/bash
) avec l'argument qui se trouve être un fichier. Dans le cas de bash ou d'un autre shell, ce fichier contient une liste de commandes à exécuter, mais maintenant nous "passons" tout ce qui va vérifier les bits d'exécution du fichier. Cette vérification n'est pas responsable de ce qui se passera plus tard.
Prenons ce cas :
$ cat hello_world | /bin/bash
… ou pour ceux qui n'aiment pas l'utilisation inutile du chat :
$ /bin/bash < hello_world
Le "shbang" #!
La séquence au début d'un fichier est juste une belle magie pour faire efficacement la même chose lorsque vous essayez d'exécuter le fichier en tant que commande. Cet article de LWN.net pourrait vous être utile :Comment les programmes sont exécutés.
Les réponses précédentes expliquent pourquoi le noexec
paramètre n'empêche pas l'exécution d'un script lorsque l'interpréteur (dans votre cas /bin/bash
) est explicitement appelée depuis la ligne de commande. Mais si c'était tout ce qu'il y avait à faire, cette commande aurait également fonctionné :
/lib64/ld-linux-x86-64.so.2 hello_world
Et comme vous l'avez noté, cela ne fonctionne pas. C'est parce que noexec
a aussi un autre effet. Le noyau n'autorisera pas les fichiers mappés en mémoire de ce système de fichiers avec PROT_EXEC
activé.
Les fichiers mappés en mémoire sont utilisés dans plusieurs scénarios. Les deux scénarios les plus courants concernent les exécutables et les bibliothèques. Lorsqu'un programme est démarré en utilisant le execve
appel système, le noyau créera en interne des mappages de mémoire pour l'éditeur de liens et l'exécutable. Toutes les autres bibliothèques nécessaires sont mappées en mémoire par l'éditeur de liens via le mmap
appel système avec PROT_EXEC
activé. Si vous avez essayé d'utiliser une bibliothèque à partir d'un système de fichiers avec noexec
le noyau refuserait de faire le mmap
appeler.
Lorsque vous avez appelé /lib64/ld-linux-x86-64.so.2 hello_world
le execve
l'appel système ne créera qu'un mappage de mémoire pour l'éditeur de liens et l'éditeur de liens ouvrira le hello_world
exécutable et tentez de créer un mappage de mémoire à peu près de la même manière qu'il l'aurait fait pour une bibliothèque. Et c'est à ce moment que le noyau refuse d'exécuter le mmap
appelez et vous obtenez l'erreur :
./hello_world: error while loading shared libraries: ./hello_world: failed to map segment from shared object: Operation not permitted
Le noexec
le paramètre permet toujours les mappages de mémoire sans autorisation d'exécution (comme c'est parfois utilisé pour les fichiers de données) et il permet également la lecture normale des fichiers, c'est pourquoi bash hello_world
travaillé pour vous.
Exécution de la commande de cette manière :
bash hello_world
vous faites bash
lire à partir du fichier hello_world
(ce qui n'est pas interdit).
Dans d'autres cas, le système d'exploitation essaie d'exécuter ce fichier hello_world
et échoue à cause de noexec
drapeau