Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

system() et open() (pipes) sûrs et portables

12 réponses
Avatar
Manuel Pégourié-Gonnard
Bonjour,

system() existe, si je comprends bien, sous deux formes essentiellement
différentes du point de vue de la sécurité : celle qui fait
potentiellement appel à un shell (l'argument est un scalaire ou une liste
à un élément), et celle qui ne fait jamais appel à un shell (l'argument
est une liste à plus d'un élément, ou il y a un premier argument suivi
sans virgule par un deuxième qui est alors interpété comme une liste).

Lorsqu'on lance une commande externe, et qu'une partie de la ligne de
commande est d'origine mal contrôlée (entrée utilisateur), si l'on veut
être sûr de savoir ce qui se passe, il faut mieux, il me semble,
utiliser la deuxième forme.

Ma question est : y a-t-il la moindre raison de ne pas préférer cette
deuxième forme, dès lors qu'on n'a pas réellement besoin d'appeler un
shell ? Par exemple en matière de portabilité (il s'agit d'un script qui
doit tourner au moins sous Unix, Windows, et Cygwin).

Même question quand il s'agit d'ouvrir une commande avec un pipe vers son
entrée standard ou depuis sa sortie standard : peut-on toujours utiliser
la forme à plus de trois arguments sur les plateformes citées ? Dans
perldoc -f open, je lis :

The last example in each block shows the pipe as "list form",
which is not yet supported on all platforms. A good rule of
thumb is that if your platform has true "fork()" (in other
words, if your platform is UNIX) you can use the list form.

qui m'inquiète un peu, parce qu'il me semble que celà exclut windows.

Visiblement il y a des trucs à lire à ce sujet dans perlipc, mais je
n'ai pas trouvé à quel endroit, je m'y attaquerai demain (ça a l'air
d'un gros morceau).

Merci d'avance pour vos conseils !

--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/

10 réponses

1 2
Avatar
Manuel Pégourié-Gonnard
Manuel Pégourié-Gonnard scripsit :

Visiblement il y a des trucs à lire à ce sujet dans perlipc, mais je
n'ai pas trouvé à quel endroit, je m'y attaquerai demain (ça a l'air
d'un gros morceau).



Finalement j'ai trouvé, mais je n'ai pas l'impression que ça répond à ma
question. D'une part parce que ce qui y est décrit comme "safe pipe
open" correspond à la syntaxe de open à plus de trois argument depuis
Perl 5.8, d'autre part parce que ça semble toujours très centré sur
Unix :

Note that these operations are full Unix forks, which means they may
not be correctly implemented on alien systems.

J'avoue que le "may not" me laisse un peu sur ma faim pour savoir si en
pratique ça marche sur windows ou pas.

J'en appele donc à votre expérience et à votre sagesse... (Et je
prévois une question subsidiaire : s'il n'y a pas moyen de gérer des
pipes sans passer par un shell sous windows, quelles sont les bonnes
pratiques concernant l'utilisation de fichiers temporaires ?)

--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
Avatar
espie
In article <hbdoth$25j8$,
Manuel Pégourié-Gonnard wrote:
Manuel Pégourié-Gonnard scripsit :

Visiblement il y a des trucs à lire à ce sujet dans perlipc, mais je
n'ai pas trouvé à quel endroit, je m'y attaquerai demain (ça a l'air
d'un gros morceau).



Finalement j'ai trouvé, mais je n'ai pas l'impression que ça répond à ma
question. D'une part parce que ce qui y est décrit comme "safe pipe
open" correspond à la syntaxe de open à plus de trois argument depuis
Perl 5.8, d'autre part parce que ça semble toujours très centré sur
Unix :

Note that these operations are full Unix forks, which means they may
not be correctly implemented on alien systems.

J'avoue que le "may not" me laisse un peu sur ma faim pour savoir si en
pratique ça marche sur windows ou pas.

J'en appele donc à votre expérience et à votre sagesse... (Et je
prévois une question subsidiaire : s'il n'y a pas moyen de gérer des
pipes sans passer par un shell sous windows, quelles sont les bonnes
pratiques concernant l'utilisation de fichiers temporaires ?)



A mon avis, il faut privilegier la securite: donc ecris des trucs qui soient
surs sous Unix... et apres, prevois des solutions de secours pour le reste.

(par exemple, tu peux assez facilement resynthetiser le system a 1 argument
a partir du systeme a plusieurs).

Pour avoir un comportement previsible, j'en suis a privilegier fork+exec
a system si, par exemple, je n'aurais besoin du shell que pour rediriger
stderr vers /dev/null.

Mais oui, effectivement, tout ce qui est fork ne tourne pas sous windows, et
il faut d'autres solutions, essentiellement a base de multi-thread pour les
machins internes, et a base de "system" simple pour le reste.

Par exemple, tu peux jeter un oeil a DBI::Proxy, qui contient des options
pour se lancer en multi-threade, explicitement pour que ca serve a quelque
chose sous windows...


Si tu t'interesses reellement a la portabilite de tes scripts, au final, tu
n'as pas le choix, tu vas etre bon pour installer perl sur cette merde de
windows et pour faire des essais...

Concernant les fichiers temporaires, il y a File::Temp, qui contient entres
autres un equivalent de mkstemp, qui est la bonne solution sur les Unix
(creation de temporaire sans race condition) et plein d'autres cochonneries.

Je t'avouerais que j'aimerais bien que ca soit nettoye un jour (si je trouve
le temps, je ferai un File::Temp::Light), car il y a a boire, a manger, et
a se pisser dessus dans File::Temp. Il n'y a qu'a voir la liste des
dependances qu'il ramene, c'est programme a la physicien, ce truc... au
point que ca m'est deja arrive d'en extraire le seul bout utile (mkstemp)
histoire d'avoir un script qui ne passe pas 3 secondes a charger tous les
trucs qui ne vont pas servir...
Avatar
espie
In article <hbemjm$10be$, Marc Espie wrote:
A mon avis, il faut privilegier la securite: donc ecris des trucs qui soient
surs sous Unix... et apres, prevois des solutions de secours pour le reste.



Post-scriptum sur la portabilite.

Ton attitude est exactement ce qu'il faut eviter, en fait (mais c'est un
travers extremement frequent).

Souvent, je croise des gens qui veulent bien faire, et qui s'inquietent du
fait que leur code devrait etre portable un peu partout, que ce soit du C,
du perl, encore autre chose. Souvent, ces gens n'ont que des notions assez
vagues de ce qui va se passer sur d'autres systemes. Et souvent, ces
gens se retrouvent a faire des compromis facheux pour que leur code puisse
eventuellement peut-etre tourner sur un systeme mythique auquel ils n'ont
pas acces...

En fait, il y a un ensemble des choses a considerer.

- les priorites dans ton cahier des charges (oui, meme si c'est pour le fun
et meme si ca n'est pas ecrit, c'est bien de se faire un cahier des charges).
Le truc le plus important, c'est pratiquement toujours d'avoir un truc qui
tourne CORRECTEMENT sur un premier systeme. Comme proprietes desirables,
il faut tres souvent mettre la clarte et la simplicite pas loin derriere,
histoire d'avoir un code maintenable (parce que de toutes facons, on va le
faire evoluer, tot ou tard, sauf si c'est un echec complet).
- la portabilite universelle n'existe pas. Il y aura toujours des systemes
avec des contraintes a la con, sans filesystem, avec des entiers bizarres,
avec pas beaucoup de memoire... faire de la portabilite sur tout et
n'importe quoi, ca ne sert a rien. Donc on se fixe un "perimetre de systemes"
sur lesquels ca doit tourner, et tant pis pour le reste. On pourra
eventuellement revisiter la decision plus tard, d'autant plus facilement
que le code sera clair.
- le code "portable" sortant directement des mains du programmeur n'existe
pas. Il n'y a que du code "porté". Tant qu'on n'a pas fait des tests sur
les systemes exotiques, on ne saura pas reellement ce qui va merder. Ce n'est
qu'avec de l'experience sur les systemes consideres qu'on ne se fera
pas avoir... typiquement, en perl, c'est plus important de bien connaitre
la bibliotheque histoire d'eviter tout appel inutile a system et de traiter
tous les noms de fichiers a coup de File::Spec que tout autre chose.
On peut deleguer un max de portabilite aux bouts de la bibliotheque standard
qui font ca. Ca evite d'avoir a se poser des questions.

En pratique, je croise des tonnes de code qui se veulent portables (souvent
autre chose que du perl, hein), avec des tonnes de trucs ou l'auteur original
s'est pris la tete pour sortir de son systeme, et qui echouent lamentablement,
parce qu'ils supposent que make est gnu-make (make -w croise recemment), ou
que sed est gnu-sed (trouve plein de sed -i dans un configure recemment,
le premier fait rire, les suivants un peu moins).

Nan, la bonne facon de faire portable, c'est reellement de mettre les mains
dans le cambouis et de porter son code sur le systeme cible... ou de
s'arreter avant, et de collaborer avec quelqu'un qui bosse sur le systeme
cible, et qui pourra pointer du doigt les vrais soucis. ;-)
Avatar
Paul Gaborit
À (at) Sun, 18 Oct 2009 02:26:01 +0200 (CEST),
Manuel Pégourié-Gonnard écrivait (wrote):
system() existe, si je comprends bien, sous deux formes essentiellement
différentes du point de vue de la sécurité : celle qui fait
potentiellement appel à un shell (l'argument est un scalaire ou une liste
à un élément), et celle qui ne fait jamais appel à un shell (l'argument
est une liste à plus d'un élément, ou il y a un premier argument suivi
sans virgule par un deuxième qui est alors interpété comme une liste).



Lorsqu'on lance une commande externe, et qu'une partie de la ligne de
commande est d'origine mal contrôlée (entrée utilisateur), si l'on veut
être sûr de savoir ce qui se passe, il faut mieux, il me semble,
utiliser la deuxième forme.



C'est la dernière syntaxe qu'il faut privilégier (celle avec un
premier élément sans virgule derrière) pour être sûr de ne pas passer
par un shell (ou un pseudo-shell).

Ma question est : y a-t-il la moindre raison de ne pas préférer cette
deuxième forme, dès lors qu'on n'a pas réellement besoin d'appeler un
shell ? Par exemple en matière de portabilité (il s'agit d'un script qui
doit tourner au moins sous Unix, Windows, et Cygwin).



Non.

Même question quand il s'agit d'ouvrir une commande avec un pipe vers son
entrée standard ou depuis sa sortie standard : peut-on toujours utiliser
la forme à plus de trois arguments sur les plateformes citées ? Dans
perldoc -f open, je lis :

The last example in each block shows the pipe as "list form",
which is not yet supported on all platforms. A good rule of
thumb is that if your platform has true "fork()" (in other
words, if your platform is UNIX) you can use the list form.

qui m'inquiète un peu, parce qu'il me semble que celà exclut windows.



En fait, sous Windows et d'autres OS, le véritable souci n'est ni
system() ni open() mais fork(). Évidemment, puisque system() et open()
font appel à fork(), on récupère les mêmes soucis qu'avec fork() mais
c'est un effet de bord.

En fait, sous Windows, le fork() est émulé en utilisant du
multi-threading. Pour en savoir plus sur les limitations de cette
émulation, il faut commencer par lire 'perlfork'. Il y a une solution
pour émuler les tubes (pipes).

L'autre solution sous Windows consiste à ne pas utiliser fork() et à
faire appel aux modules Win32 qui permettent de créer de vrais
processus externes. En revanche, la communication entre les processus
ne pourra pas reposer sur des tubes....

--
Paul Gaborit - <http://perso.mines-albi.fr/~gaborit/>
Perl en français - <http://perl.mines-albi.fr/>
Avatar
Paul Gaborit
À (at) Sun, 18 Oct 2009 09:33:02 +0000 (UTC),
(Marc Espie) écrivait (wrote):
In article <hbemjm$10be$, Marc Espie wrote:
A mon avis, il faut privilegier la securite: donc ecris des trucs qui soient
surs sous Unix... et apres, prevois des solutions de secours pour le reste.



Post-scriptum sur la portabilite.

Ton attitude est exactement ce qu'il faut eviter, en fait (mais c'est un
travers extremement frequent).


[...]
- la portabilite universelle n'existe pas. Il y aura toujours des systemes
avec des contraintes a la con, sans filesystem, avec des entiers bizarres,
avec pas beaucoup de memoire... faire de la portabilite sur tout et
n'importe quoi, ca ne sert a rien. Donc on se fixe un "perimetre de systemes"
sur lesquels ca doit tourner, et tant pis pour le reste. On pourra
eventuellement revisiter la decision plus tard, d'autant plus facilement
que le code sera clair.


[...]

Je suis complètement d'accord avec ce discours. Ceci étant, on peut
quand même distinguer différents périmètres de portabilité. Si on se
limite aux Unix (Linux, *BSD), à MacOS X et Windows, on n'est pas dans
la même situation que si on souhaite attaquer aussi des smartphones ou
des ordinateurs de bord... En reprenant tes exemples, on a de la
mémoire, on a un filesystem, on a des entiers décents et on a une
puissance de calcul décente. De plus Perl (ou plutôt, perl) intègre
beaucoup de mécanismes permettant de garantir une certaine portabilité
sans écriture de code spécifique.

Nan, la bonne facon de faire portable, c'est reellement de mettre les mains
dans le cambouis et de porter son code sur le systeme cible... ou de
s'arreter avant, et de collaborer avec quelqu'un qui bosse sur le systeme
cible, et qui pourra pointer du doigt les vrais soucis. ;-)



Alors là : d'accord à 120% ! ;-)

Mais si on écrit le bout de code initial sous Unix, on peut quand même
préparer le terrain en isolant dans le code les parties qu'on connaît
déjà comme pouvant poser problème lors du portage.

--
Paul Gaborit - <http://perso.mines-albi.fr/~gaborit/>
Perl en français - <http://perl.mines-albi.fr/>
Avatar
Manuel Pégourié-Gonnard
Marc Espie scripsit :

A mon avis, il faut privilegier la securite: donc ecris des trucs qui
soient surs sous Unix... et apres, prevois des solutions de secours
pour le reste.



Malheureusement, pour le script que je suis en train de revoir dans
cette optique de sécurité (je suis conscient que déjà ça, ce n'est pas
idéal : il faut mieux concevoir en pensant à la sécurité que revoir
après-coup, on est d'accord) le cahier des charges est clair : ça doit
tourner sous les Unix (Linux, *BSD, Mac OS X) et sous Windows.

(par exemple, tu peux assez facilement resynthetiser le system a 1 argument
a partir du systeme a plusieurs).



Je ne suis pas sûr d'avoir compris ce que tu veux dire ici.

Mais oui, effectivement, tout ce qui est fork ne tourne pas sous
windows,



Noté. Je trouve dommage que la doc ne s'exprime pas aussi clairement. Au
lieu de "may not work on some systems", ils pourraient ajouter, "doesn't
work on windows", histoire qu'on soit fixés.

et il faut d'autres solutions, essentiellement a base de
multi-thread pour les machins internes, et a base de "system" simple
pour le reste.



Ce que tu appelles le "system" simple, c'est celui qui passe par un
shell ? Dans ce cas, comment tu fais pour t'assurer que tu lances juste
la commande que tu veux lancer et pas des saloperies induites par les
arguments ? Je ne sais pas comment il faudrait quoter et échapper les
arguments sous windows pour éviter toute injection (un équivalent de
myslq_real_escape_string du PHP, adapté au shell windows, serait le
bienvenu).

Par exemple, tu peux jeter un oeil a DBI::Proxy, qui contient des options
pour se lancer en multi-threade, explicitement pour que ca serve a quelque
chose sous windows...



Là, c'est pour un truc externe, mais je note pour une autre fois.

Si tu t'interesses reellement a la portabilite de tes scripts, au final, tu
n'as pas le choix, tu vas etre bon pour installer perl sur cette merde de
windows et pour faire des essais...



J'ai déjà un windows qui traine dans un coin à cette fin, il y a même
des bouts de Perl dessus. Ce que je crains, c'est d'avoir un truc qui
marcherait chez moi pour une raison X ou Y mais pas en général. (J'ai
l'impression qu'il y a moins de compatibilités entre deux version de
Windows qu'entre deux Unix très éloignés.)

Concernant les fichiers temporaires, il y a File::Temp, qui contient
entres autres un equivalent de mkstemp, qui est la bonne solution sur
les Unix (creation de temporaire sans race condition) et plein
d'autres cochonneries.



J'en déduis que ce n'est pas pour windows ?

Je t'avouerais que j'aimerais bien que ca soit nettoye un jour (si je trouve
le temps, je ferai un File::Temp::Light), car il y a a boire, a manger, et
a se pisser dessus dans File::Temp. Il n'y a qu'a voir la liste des
dependances qu'il ramene, c'est programme a la physicien, ce truc... au
point que ca m'est deja arrive d'en extraire le seul bout utile (mkstemp)
histoire d'avoir un script qui ne passe pas 3 secondes a charger tous les
trucs qui ne vont pas servir...



Ça ne donne pas très envie, en effet...

Merci pour tous tes conseils, je vais creuser ça.

--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
Avatar
Manuel Pégourié-Gonnard
Marc Espie scripsit :

Post-scriptum sur la portabilite.

Ton attitude est exactement ce qu'il faut eviter, en fait (mais c'est un
travers extremement frequent).



Aille. Pour le cas qui m'intéresse en ce moment, le cahier des charges
est clair, ça doit tourner sur des postes Unix et Windows (et cet
hybride qu'est Cygwin). Du coup, pour le moment, je vais continuer à
travailler dans ce sens.

Par contre, j'ai lu attentivement tes remarques ainsi que celles de
Paul. En effet je me rends compte que je n'avais en général pas la
meilleure attitude sur le sujet, il va falloir que j'y repense.

Donc, même si pour le moment j'ai l'air de persister dans mon attitude,
c'est parce que je m'intéresse à un cas particulier qui doit tourner
aussi sous windows et que j'aimerais *vraiment* arriver à faire
fonctionner correctement, mais vos remarques, à plus long terme, ne sont
pas tombées dans l'oreille d'un sourd.

--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
Avatar
Manuel Pégourié-Gonnard
Paul Gaborit scripsit :

C'est la dernière syntaxe qu'il faut privilégier (celle avec un
premier élément sans virgule derrière) pour être sûr de ne pas passer
par un shell (ou un pseudo-shell).



Ok.

Ma question est : y a-t-il la moindre raison de ne pas préférer cette
deuxième forme, dès lors qu'on n'a pas réellement besoin d'appeler un
shell ? Par exemple en matière de portabilité (il s'agit d'un script qui
doit tourner au moins sous Unix, Windows, et Cygwin).



Non.



Parfait.

En fait, sous Windows et d'autres OS, le véritable souci n'est ni
system() ni open() mais fork(). Évidemment, puisque system() et open()
font appel à fork(), on récupère les mêmes soucis qu'avec fork() mais
c'est un effet de bord.



C'est ce que j'avais cru comprendre.

En fait, sous Windows, le fork() est émulé en utilisant du
multi-threading. Pour en savoir plus sur les limitations de cette
émulation, il faut commencer par lire 'perlfork'. Il y a une solution
pour émuler les tubes (pipes).



Bien, je vais lire ça.

L'autre solution sous Windows consiste à ne pas utiliser fork() et à
faire appel aux modules Win32 qui permettent de créer de vrais
processus externes. En revanche, la communication entre les processus
ne pourra pas reposer sur des tubes....



Noté, mais ça me paraît gênant de ne pas avoir de tubes.

--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
Avatar
Paul Gaborit
À (at) Sun, 18 Oct 2009 14:11:13 +0200 (CEST),
Manuel Pégourié-Gonnard écrivait (wrote):
Noté, mais ça me paraît gênant de ne pas avoir de tubes.



On peut simuler cela avec du TCP/IP (en local).

--
Paul Gaborit - <http://perso.mines-albi.fr/~gaborit/>
Perl en français - <http://perl.mines-albi.fr/>
Avatar
Manuel Pégourié-Gonnard
Paul Gaborit scripsit :

En fait, sous Windows, le fork() est émulé en utilisant du
multi-threading. Pour en savoir plus sur les limitations de cette
émulation, il faut commencer par lire 'perlfork'. Il y a une solution
pour émuler les tubes (pipes).



Mais ça a l'air très bien ça ! (Je viens de le lire, mais ne l'ai pas
encore testé.) Du coup, une question sans doute idiote me vient :
pourquoi ceci (la « list fom of pipe open ») n'est pas implémenté
directement, puisque (si j'ai bien compris et sous réserve des tests à
venir) c'est techniquement faisable depuis Perl 5.8, d'une façon qui est
même indiquée dans la doc ?

--
Manuel Pégourié-Gonnard Institut de mathématiques de Jussieu
http://weblog.elzevir.fr/ http://people.math.jussieu.fr/~mpg/
1 2