Je suis Charlie

Autres trucs

Accueil

Seulement les RFC

Seulement les fiches de lecture

Mon livre « Cyberstructure »

Ève

Ce blog n'a d'autre prétention que de me permettre de mettre à la disposition de tous des petits textes que j'écris. On y parle surtout d'informatique mais d'autres sujets apparaissent parfois.


La ferme de compilation Cfarm

Première rédaction de cet article le 1 décembre 2023


Si vous êtes programmeuse ou programmeur, vous avez peut-être des problèmes de portabilité de vos programmes, et vous souhaitez les tester sur d'autres plate-formes que celles où vous avez accès habituellement. C'est le but des fermes de compilation et cet article va présenter la Cfarm, sur laquelle je viens d'avoir un compte.

Donc, le principe d'une ferme de compilation : beaucoup de machines, de types et de systèmes d'exploitation différents, auxquelles vous pouvez accéder via SSH et tester vos programmes. C'est surtout utile pour les langages de bas niveau comme C, il y a sans doute moins de problèmes de portabilité en Python ou Haskell

La Cfarm est réservée aux programmes libres. Pour avoir un compte, vous devez remplir un formulaire où vous indiquez notamment les projets de logiciel libre que vous maintenez. Votre compte est ensuite approuvé (dans mon cas, cela avait pris moins de 24 heures) et vous pouvez alors vous connecter sur le site Web, et indiquer des clés SSH qui seront automatiquement copiées sur toutes les machines, vous permettant de vous connecter (mais vous ne serez évidemment pas root). La liste des machines est longue ! Et elle contient des types devenus rares, par exemple des processeurs PowerPC, qui sont utiles car ce sont dans les rares à être gros-boutiens. J'aime bien aussi la machine ARM faisant tourner FreeBSD. Et, oui, il y a une RISC-V, si vous ne voulez pas en acheter une.

Pour vous simplifier la vie, si vous travaillez sur plusieurs de ces machines, OpenSSH permet des configurations globales, voici mon ~/.ssh/config :

Host *.cfarm.net
   User bortzmeyer
   IdentityFile ~/.ssh/id_cfarm
  

Notez que j'utilise une clé ed25519 mais la documentation note bien que certains vieux systèmes ne connaissent pas cet algorithme, et elle suggère d'utiliser RSA, si on veut passer partout (on peut avoir plusieurs clés dans sa configuration Cfarm).

Cette configuration est également utile si la machine utilise un port inhabituel, ce que font certaines.


L'article seul

Fiche de lecture : La réputation

Auteur(s) du livre : Laure Daussy
Éditeur : Les échappés
978-2-35766-198-1
Publié en 2023
Première rédaction de cet article le 26 novembre 2023


Ce livre parle d'un féminicide, l'assassinat de Shaïna Hansye en 2019. Et regarde de près l'environnement où ce crime a eu lieu. Ce n'était pas un fait isolé, mais le produit de plusieurs phénomènes, dont le sexisme et les injonctions à la « pureté » contre les filles et femmes.

Le meurtre a eu lieu à Creil et l'assassin était le « petit ami » de la victime. La journaliste Laure Daussy est allé interroger les parents de Shaïna, ses amies, les enseignants, les associations pour comprendre ce qui s'est passé. Creil cumule les problèmes : services publics supprimés ou défaillants, chômage massif, montée de l'islamisme, etc. Pour ne citer qu'un exemple, la maternité, où les jeunes filles pouvaient avoir des conseils sur la contraception et l'IVG a fermé, les habitantes sont censées aller à Senlis, ville avec laquelle il n'y a pas de liaison par transports en commun. Les jeunes filles sans permis de conduire sont donc privées de toute information, sauf si leurs parents acceptent de les emmener (ce qui n'est pas toujours facile à demander !). Les « territoires perdus de la République », dont les médias aiment bien parler, c'est aussi ça : une ville abandonnée des pouvoirs publics. Un autre exemple est donné par l'incroyable passivité de la police lorsque Shaïna, deux ans avant le meurtre, était allé porter plainte pour un viol. Beaucoup de femmes n'osent pas porter plainte, craignant (à juste titre) qu'elles se retrouvent accusées ou stigmatisées ou bien que cela ne serve à rien. Ici, l'enquête de la police et l'instruction judiciaire se sont déroulées avec lenteur, le jugement n'ayant eu lieu qu'après le meurtre de la victime.

Mais l'un des principaux problèmes n'est pas directement lié à cet abandon. C'est la question de la réputation, qui donne son titre à l'enquête. Des hommes ont décidé de contrôler la vie des femmes et notamment par le biais du contrôle de leur vie affective et sexuelle. Une jeune fille qui vit un peu librement peut vite se retrouver marquée par l'étiquette « fille facile ». Souvent, il n'est même pas nécessaire de vouloir vivre sa vie, toute fille peut se retrouver ainsi étiquetée, par exemple parce qu'un homme a voulu se « venger » d'elle. Si personne, parlant à la journaliste, n'a osé défendre ouvertemement le meurtre de Shaïna, en revanche plusieurs de ses interlocuteurs ont relativisé ce meurtre, le justifiant par le statut de « fille facile » de la victime.

Le terme revient souvent lorsque les hommes (et parfois aussi les femmes) parlent à l'auteure. Le poids des préjugés, celui, grandissant, de la religion, le sexisme se combinent pour enfermer les femmes de Creil. Toutes portent en permanence le poids de cette injonction à la réputation.


L'article seul

Modifier un message entrant en Python

Première rédaction de cet article le 24 novembre 2023


Un peu de programmation aujourd'hui. Supposons qu'on reçoive des messages qui ont été modifiés en cours de route et qu'on veut remettre dans leur état initial. Comment faire cela en Python ?

Comme exemple, on va supposer que les messages contenant le mot « chiffrer » ont été modifiés pour mettre « crypter » et qu'on veut remettre le terme correct. On va écrire un programme qui reçoit le message sur son entrée standard et met la version corrigée sur la sortie standard. D'abord, la mauvaise méthode, qui ne tient pas compte de la complexité du courrier électronique :

import re
import sys

botched_line = re.compile("^(.*?)crypter(.*?)$")

for line in sys.stdin:
    match = botched_line.search(line[:-1])
    if match:
        # re.sub() serait peut-être meilleur ?
        newcontent = match.group(1) + "chiffrer" + match.group(2) + "\n"
    else:
        newcontent = line 
    print(newcontent, end="")
  

On lit l'entrée standard, on se sert d'une expression rationnelle (avec le module re) pour trouver les lignes pertinentes, et on les modifie (au passage, le point d'interrogation à la fin des groupes entre parenthèses est pour rendre l'expression non gourmande). Cette méthode n'est pas bonne car elle oublie :

Il faut donc faire mieux.

Il va falloir passer au module email. Il fournit tout ce qu'il faut pour analyser proprement un message, même complexe :

import sys
import re
import email
import email.message
import email.policy
import email.contentmanager

botched_line = re.compile("^(.*?)crypter(.*?)$")

msg = email.message_from_file(sys.stdin, _class=email.message.EmailMessage,
                              policy=email.policy.default)
for part in msg.walk():
    if part.get_content_type() == "text/plain":
        newcontent = ""
        for line in part.get_content().splitlines():
            match = botched_line.search(line)
            if match:
                # re.sub() serait peut-être meilleur ?
                newcontent += match.group(1) + "chiffrer" + match.group(2) + "\n"
            else:
                newcontent += line + "\n"
        email.contentmanager.raw_data_manager.set_content(part, newcontent)
print(msg.as_string(unixfrom=True))
  

Ce code mérite quelques explications :

  • email.message_from_file lit un fichier (ici, l'entrée standard) et rend un objet Python de type message. Attention, par défaut, c'est un ancien type, et les opérations suivantes donneront des messages d'erreur incompréhensibles (comme « AttributeError: 'Compat32' object has no attribute 'content_manager' » ou « AttributeError: 'Message' object has no attribute 'get_content'. Did you mean: 'get_content_type'? »). Les paramètres _class et policy sont là pour produire des messages suivant les types Python modernes.
  • walk() va parcourir les différentes parties MIME du message, récursivement.
  • get_content_type() renvoie le type MIME de la partie et nous ne nous intéressons qu'aux textes bruts, les autres parties sont laissées telles quelles.
  • get_content() donne accès aux données (des lignes de texte) que splitlines() va découper.
  • Si l'expression rationnelle correspond au motif donné, on ajoute la version modifiée, sinon on ne touche à rien.
  • set_content() remplace l'ancien contenu par le nouveau.
  • Et enfin, as_string transforme l'objet Python en texte. Notre message a été transformé.

Ce code peut s'utiliser, par exemple, depuis procmail, avec cette configuration :

:0fw
| $HOME/bin/repair-email.py
  

Évidemment, il peut être prudent de sauvegarder le message avant cette manipulation, au cas où. En procmail :

:0c:
Mail/unmodified
  

Vous voulez tester (sage précaution) ? Voici un exemple de message, fait à partir d'un spam reçu :


From spammer@example  Fri Nov 24 15:08:06 2023
To: <stephane@bortzmeyer.org>
MIME-Version: 1.0
Content-Type: multipart/alternative; 
        boundary="----=_Part_9748132_1635010878.1700827045631"
Date: Fri, 24 Nov 2023 13:42:17 +0100 (CET)
Subject: 🍷 Catalogues vins
From: Ornella FEDI <ornella.fedi@vinodiff.com>
Content-Length: 65540
Lines: 2041

------=_Part_9748132_1635010878.1700827045631
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

Cet E-mail dit "crypter". Pour pouvoir afficher cet E-mail, le clie=
nt de messagerie du destinataire doit supporter ce format.
------=_Part_9748132_1635010878.1700827045631
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<?xml version=3D"1.0" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns=3D"http://www.w3.org/1999/xhtml">
  <head>
    <title>E-Mail</title>
<style type=3D"text/css" media=3D"screen">
</style>
  </head>
<body>Deleted to save room</body></html>
------=_Part_9748132_1635010878.1700827045631--

  

Mettez-le dans un fichier, mettons spam.email et passez le au programme Python :


% cat /tmp/spam.email | ./repair.py
From spammer@example  Fri Nov 24 15:08:06 2023
To: <stephane@bortzmeyer.org>
MIME-Version: 1.0
Content-Type: multipart/alternative; 
        boundary="----=_Part_9748132_1635010878.1700827045631"
Date: Fri, 24 Nov 2023 13:42:17 +0100 (CET)
...

------=_Part_9748132_1635010878.1700827045631
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable

Cet E-mail dit "chiffrer". Pour pouvoir afficher cet E-mail, le client de mes=
sagerie du destinataire doit supporter ce format.

------=_Part_9748132_1635010878.1700827045631
...

  

Si vous voulez améliorer ce programme, vous pouvez gérer les cas :

  • Plusieurs « crypter » sur une ligne.
  • Remplacer la concaténation + par quelque chose de plus rapide (je cite Bertrand Petit : concaténer des chaines en Python est couteux. Chaque application de l'opérateur + crée une nouvelle chaîne qui n'aura qu'une existence brève, et cela cause aussi, à chaque fois, la copie de l'intéralité du contenu de la chaine en croissance. À la place, il vaudrait mieux stocker chaque bout de chaine dans une liste, pour finir par tout assembler en une fois, par exemple avec join).

L'article seul

Capitole du Libre 2023, et mon exposé sur la censure de l'Internet

Première rédaction de cet article le 22 novembre 2023


Les 18 et 19 novembre 2023, c'était Capitole du Libre à Toulouse. Plein d'exposés intéressants et de rencontres passionnantes autour du thème du logiciel libre. capitole-du-libre-2023-garonne.jpg

C'est gros, le Capitole du Libre : 1 200 personnes sont venues, il y avait 118 orateurices, 94 conférences, 18 ateliers et 31 stands. Comme il y a eu davantage de propositions que de créneaux, certaines ont été refusées. 50 kg de pop-corn ont été légués par le DevFest dont 20 ont été consommés. Mais il y avait aussi des pains au chocolat : capitole-du-libre-2023-pains-chocolat.jpg

Personnellement, j'ai fait un exposé sur la censure sur l'Internet. Les supports au format PDF sont capitole-libre-2023-censure.pdf (et leur source en capitole-libre-2023-censure.tex). La vidéo a été faite mais n'est pas encore publiée. (Amusant calendrier, cet exposé sur la censure a été fait deux jours avant la publication du RFC 9505.)

Ironie de la situation, le réseau de l'ENSEEIHT, l'école qui héberge gratuitement le Capitole du Libre, met en œuvre une des techniques de censure présentées. Si on tente de se connecter à OnlyFans, on récupére un RST (ReSeT) TCP, probablement suite à l'examen du SNI dans l'ouverture de la session TLS. Vu par tcpdump, voici ce que ça donne (le dernier paquet, le RST, n'est pas légitime) :

13:19:35.603931 IP 172.22.223.35.40874 > 54.211.0.120.443: Flags [S], seq 2413541568, win 64240, options [mss 1460,sackOK,TS val 46581492 ecr 0,nop,wscale 7], length 0
13:19:35.744066 IP 54.211.0.120.443 > 172.22.223.35.40874: Flags [S.], seq 3076610656, ack 2413541569, win 26847, options [mss 1460,sackOK,TS val 453378101 ecr 46581492,nop,wscale 8], length 0
13:19:35.744173 IP 172.22.223.35.40874 > 54.211.0.120.443: Flags [.], ack 1, win 502, options [nop,nop,TS val 46581633 ecr 453378101], length 0
13:19:35.830075 IP 172.22.223.35.40874 > 54.211.0.120.443: Flags [P.], seq 1:518, ack 1, win 502, options [nop,nop,TS val 46581719 ecr 453378101], length 517
13:19:35.833848 IP 54.211.0.120.443 > 172.22.223.35.40874: Flags [R.], seq 1, ack 518, win 502, length 0
  

Et ne dites pas d'utiliser Tor, les nœuds d'entrée publics sont bloqués.

Maintenant, les bons exposés (dans l'ordre arbitraire que j'ai décidé tout seul avec mes préférés en premier). Vincent Privat a présenté un système pour récupérer automatiquement les photos que publie la NASA (avec leurs métadonnées) et les mettre sur Wikimedia Commons. C'est légal puisque la NASA, comme toutes les agences fédérales étatsuniennes, met tout ce qu'elle publie dans le domaine public. Mais c'est techniquement difficile. Déjà, s'il y a parfois une API pour lister les images à récupérer, parfois, il faut plutôt scraper l'HTML. Et parfois il faut deviner l'URL en testant plusieurs variantes. D'autre part, il faut éliminer les doublons (chacun peut écrire dans Wikimedia Commons et ces images sont souvent importées). Son outil récupère toutes les métadonnées, les vérifie et convertit vers ce qu'attend Wikimedia Commons. Comme toujours avec l'open data, il y a des bavures amusantes, par exemple la date de prise de vue indiquée est parfois dans le futur, il y a du mojibake dans les textes, des licences incohérentes (entre le texte et les EXIF), des « descriptions » qui sont en fait de la publicité, etc (tous des problèmes classiques de l'open data). D'autre part, beaucoup d'outils de Wikimedia sont conçus pour le texte, car la cible est Wikipédia. Utiliser les outils et machines de Wikimedia pour traiter des images de beaucoup de giga-octets (par exemple pour le dédoublonnage) explosait les quotas. Enfin, pour dédoublonner, le programme calcule un perceptual hashing, pas un simple et traditionnel condensat.

À noter que les autres agences spatiales ne mettent rien sous licence libre donc la vision de l'espace est dominée par celle des États-Unis, les seuls à avoir compris la force du soft power.

Julian Vanden Broeck a fait un trés bon bilan de son changement du système de messagerie instantanée interne dans son entreprise (passage à Matrix). Comment ça marche en vrai ? Quel accompagnement pour les utilisateurices ? Quelle acceptation ? Entre utilisateurices qui râlaient contre l'ancien système, celleux qui râlaient contre le nouveau, celleux qui disaient que Microsoft Teams, c'était mieux, et celleux qui critiquaient la couleur des boutons, l'acceptation n'était pas évidente. La technique, c'est facile, ce sont les humains qui sont compliqué·es. « On a désactivé le chiffrement systématique car c'était trop frustrant pour les utilisateurs, notamment en cas de perte de clés. » Je confirme que c'est aussi mon expérience avec Matrix, loin des promesses d'un système parfait. Et encore, l'entreprise de l'orateur n'utilise que le client Element, les problèmes sont pires quand on a plusieurs clients (de ce point de vue, Matrix ne marque aucun progrès par rapport à XMPP, où l'incompatibilité des clients a toujours été une plaie). Au passage, si vous voulez essayer, je suis joignable à @bortzmeyer:matrix.underworld.fr.

On s'est bien amusés avec l'exposé « Modèles d'organisation ouverts dans les entreprises du logiciel libre » car les deux orateurs ne se sont pas présentés, sans explication. Thomas Petazzoni a alors lancé l'idée que, puisque le sujet était les organisations ouvertes, non hiérarchiques, etc, on n'avait pas besoin d'orateurs et on pouvait débattre entre nous. Ça a très bien pris et il y a eu une discussion intéressante, par exemple avec les gens d'une association, l'Échappée Belle, qui est en fait une coopérative de freelances, où chaque salarié décide de son salaire. Évidemment, ils insistaient sur le fait que ça ne peut sans doute marcher que dans les petites organisations (ils sont six), où les gens se connaissent et ont des valeurs communes.

Et Pierre-Yves Lapersonne a posé la question du choix des bibliothèques logicielles quand on développe un logiciel libre. Le ou la programmeureuse ne peut pas tout faire, il faut donc dépendre de bibliothèques existantes. Vous, je ne sais pas mais moi, quand je commence un projet, j'ai toujours du mal à identifier les bonnes bibliothèques dont mon projet va dépendre. Vous utilisez quoi, comme critères ? Quand il y a le choix, c'est compliqué. Comment choisir, et, par exemple, détecter celles qui ne sont plus maintenues (donc où les bogues ne seront plus corrigées), celles gérées par une boite qui va subitement changer la licence (il y a eu deux-trois cas spectaculaires ces dernières années, comme MongoDB), etc. Parmi les critères possibles (mais aucun n'est parfait et l'auteur recommande de se méfier des critères quantitatifs simples) :

  • la licence utilisée, bien sûr,
  • le langage de programmation utilisé,
  • les dépendances de cette bibliothèque,
  • le nombre de tickets, s'il est faible, c'est peut-être qu'il n'y a pas beaucoup d'utilisateurs, si beaucoup sont fermés, cela peut être bon signe (bogues corrigées rapidement) ou mauvais signe (mainteneur qui ferme tout de suite sans corriger),
  • la date du dernier ticket fermé peut être un bon indicateur pour savoir si le projet vit toujours,
  • nombre de commits (pas très pertinent car il dépend du style de travail des programmeurs, certain·es font dix commits dans la matinée, d'autres un par semaine (pensez à git rebase…),
  • date du dernier commit pour détecter les codes abandonnés (ou bien tellement parfaits qu'ils n'ont pas besoin de changements…),
  • l'activité du mainteneur ; est-ce qu'il est actif (voir son profil sur la forge publique utilisée) avec le langage du projet (un projet Go créé par quelqu'un qui ne fait que du Rust aujourd'hui risque de de ne pas faire l'objet de beaucoup d'attention),
  • l'existence de tests, et la couverture du code par la suite de tests (pour avoir une base saine si on va devoir modifier le logiciel).

On a aussi discuté d'UI et d'UX au Capitole du Libre. Nojhan a parlé de LiquidPrompt, un des nombreux mécanismes permettant d'avoir des invites très riches pour la ligne de commande du shell Unix. L'orateur a commencé par : « vous utilisez toustes le shell ? », ce qui évidemment fait rire au Capitole du Libre. Le but du projet est d'améliorer l'invite de la ligne de commande en indiquant davantage d'informations. Le problème est évidemment de ne pas surcharger l'utilisateurice. LiquidPrompt, comme ses concurrents, permet d'avoir plusieurs thèmes, et le thème par défaut est dense et plein d'informations. (Y compris la charge de la batterie, ce que je trouve anxiogène.) Pour les démonstrations, c'est spectaculaire. Une autre démonstration montrait un thème où l'invite occupe tout le terminal tellement il y a d'informations.

Un gros avantage de ces systèmes d'invites riches est l'indication de l'état du dépôt Git (ou bien parfois d'autres VCS) où on se trouve. On peut indiquer la branche, bien sûr (combien de fois je me suis planté, me trompant sur la branche où j'étais) mais aussi des suggestions sur les commandes git qui pourraient être pertinentes.

Bon, il y a tellement de systèmes d'invites shell améliorées que c'est un travail en soi d'en choisir une ! L'auteur a fait un article de comparaison.

Maïtané Lenoir a parlé de « modération » (terme que je trouve contestable) sur les réseaux sociaux, notamment les « réseaux sociaux capitalistes » (ce qui inclut TikTok, même si ça déplait au PC chinois). « Je vais vous raconter des trucs traumatiques. » Déjà, elle a noté qu'il fallait bien différencier la politique officiellement affichée, et les moyens qui sont effectivement mis en œuvre pour l'appliquer. (Ce principe doit pouvoir s'appliquer à la politique en général.) Elle a insisté sur l'importance d'une riposte graduée. Si un logiciel ne permet que des réactions simplistes et binaires (supprimer le compte, par exemple), le contrôle sera difficile car on n'aura le choix qu'entre deux mauvaises décisions. Parmi les difficultés (que les réseaux sociaux capitalistes mettent sous le tapis) du contrôle, la langue. Il ne suffit pas de lire la langue utilisée, il faut aussi connaitre la culture, pour comprendre allusions, jeux de mots, choix de vocabulaire. Un Français va avoir du mal à évaluer du contenu écrit en français par un Ivoirien ou un Québecois.

Et, bien sûr, le contrôle prend du temps et des efforts et les réseaux sociaux capitalistes, quoi qu'ils prétendent, ne mettent pas assez de moyens. L'IA n'est pas une solution. (Notez que Pharos a la même analyse.)

L'auteure travaille dans l'équipe de modération de Framasoft, par exemple pour l'instance fédivers Framapiaf. Inutile de dire que ce n'est pas facile, cette équipe a dû faire face à des campagnes s'attaquant à Framasoft et à sa modération (prétendant par exemple qu'il n'y en avait pas). La publication de la charte de modération n'a rien arrangé, les attaques des ultras du contrôle contre Framasoft ont redoublé, tournant au harcèlement. Heureusement qu'en interne, le collectif était soudé.

L'oratrice a beaucoup insisté sur les difficultés pratiques, quotidiennes de la modération : par exemple, beaucoup de signalements ne donnent aucun détail. C'est quoi, le problème, exactement ? Les modérateurs doivent essayer de deviner. Signaleurs, détaillez vos messages ! (Sinon, elle n'en a pas parlé, mais je trouve qu'il y a aussi des signalements malveillants, pour faire taire des ennemis politiques. Les islamistes font souvent cela, par exemple.) Conseil : ne pas oublier le temps et l'énergie consommée. Certes, la modération de Framapiaf n'est pas parfaite (aucune ne l'est) mais un des principes est : « ne pas passer un temps infini sur chaque cas » (surtout que les modérateurs sont bénévoles). Or, les signaleurs sont souvent très exigeants « on veut une action safe tout de suite !!! »

La conclusion : Modération = Pouvoir et Humain = Faillible.

On a eu évidemment une table ronde sur l'IA, où on a surtout parlé de régulation. Celle-ci évidemment ne vise pas forcément qu'à protéger les gens, elle peut avoir des motivations protectionnistes. Florence Sèdes a noté que le récent projet Kyutai de Niel et Saadé ne mette en jeu que des sommes « dérisoires ». Toujours dans l'actualité chargée de l'IA, j'ai demandé à ChatGPT les raisons du licenciement spectaculaire de la direction d'OpenAI, la veille du Capitole du Libre, licenciement qui fait parler toute la Silicon Valley mais il m'a répondu qu'il ne savait pas, qu'à sa connaissance, Altman était toujours CEO. chatgtp-altman.png

L'autre table ronde portait sur la sobriété, avec notamment Agnès Crepet, qui bosse chez Fairphone. La table ronde a été un peu plus animée car il y avait aussi le député Modem Philippe Latombe, spécialiste des questions numériques, mais qui a voté la loi JO, avec l'extension de la vidéo-surveillance. Indépendamment de la question des droits humains, la vidéo-surveillance est un gouffre énergétique, il s'est fait interpeller là-dessus, a agité le chiffon rouge en disant qu'il ne fallait pas dire vidéo-surveillance mais vidéo-protection (comme on dit dans les comptes rendus de débats « mouvements divers ; protestations »), bref, a contribué à rappeler que la sobriété numérique n'était pas consensuelle. Les JO, qui sont le prétexte pour généraliser cette surveillance, sont une grande source de gaspillage, et Bookynette, qui animait la table ronde, a appelé à boycotter cette fête du fric et du dopage.

Sinon, en matière d'empreinte environnementale du numérique, ne pensez pas qu'à la consommation électrique, note Agnès Crepet. Il y a aussi l'extraction des métaux, dans des conditions désastreuses. Elle a aussi parlé de la réparabilité, sa tante Ginette étant capable de démonter un ordiphone. Ginette est plus habile de ses mains que moi, elle arrive à réparer son Fairphone, alors que j'ai bousillé un Fairphone 2 en essayant d'intervenir à l'intérieur.

Autre participant à la table ronde, Gaël Duval (/e/). Il a parlé de l'obsolescence logicielle. Pour limiter la surconsommation de matériel, il faut aussi maintenir du logiciel qui continue à tourner sur les « vieux » (10 ans) équipements. Par exemple, les vieux trucs 32 bits ont du mal avec des applications qui sont en 64 bits maintenant. Philippe Latombe note que l'État n'est pas forcément cohérent, par exemple en prônant la réutilisation tout en taxant (avec le racket « copie privée ») les téléphones reconditionnés. Agnès Crepet a appelé au « techno-discernement » ; la technique n'est pas forcément utile. Parfois, la solution est de ne pas numériser.

Guillaume a parlé de l'intégration des contributions externes dans le compilateur Rust. Il a expliqué comment les auteurs de Rust géraient ces contributions, entre le désir de favoriser les contributions extérieures (un gros point fort du logiciel libre) et la nécessité de contrôler (pour éviter bogues, voire, pires, attaques « supply chain »). L'équilibre est délicat. Une bonne partie du processus d'examen des contributions est automatisée, via la CI. Si le changement est léger et que la CI passe, ça va plus vite. Sinon, jetez un coup d'œil à la file d'attente de Rust. Évidemment, il faut que tous les tests passent et, rien que pour les messages d'erreur du compilateur, il y a plusieurs dizaines de milliers de tests. Les machines tournent longtemps ! Si vous voulez contribuer au compilateur Rust, certains tickets sont marqués « pour débutants » et sont a priori plus faciles. Vous pouvez voir pas mal d'articles sur Rust au blog de l'orateur.

Adrien Destugues a fait un exposé très geek, la rétro-ingéniérie d'un jeu vidéo où il jouait quand il était petit (le logiciel ludo-éducatif « Lectures Enjeu »), jeu commercial abandonné et qu'il voulait faire revivre. Aucune perspective utilitaire ou financière, c"était juste de l'art pour l'art. Amusant, les chaines de caractères du jeu sont paramétrées avec un @ qui est remplacé par une marque du féminin si la joueuse a indiqué qu'elle était du genre féminin. "arrivé@" dans le fichier est affiché "arrivé" ou "arrivée" selon le cas.

Un sujet financier ? « La blockchain pour le financement du logiciel libre » par Guillaume Poullain, par exemple. Gitcoin est une organisation de financement du logiciel libre (50 M$ distribués en 2023). Ça utilise le protocole Allo (je ne connais pas). Les gens qui financent ont un droit de vote. L'allocation est quadratique, favorisant le nombre de votes davantage que le montant (pour éviter qu'un millionaire n'ait autant de poids que mille smicards). 3 715 projets ont été financés, 3,8 M$ de donations (je n'ai pas vérifié tous les chiffres).

Un des problèmes est évidemment ce sont les projets les plus connus (souvent les plus riches) qui ont le plus de votes. Et, évidemment, certains projets reçoivent peu de votes et donc de fric. Il y a aussi le risque d'attaque Sybil, favorisé par l'allocation quadratique. (Solution : une vérification d'unicité avec un système nommé Passport.)

À noter qu'on ne peut pas retirer l'argent, le flux est dans un seul sens (ce qui évite les bogues style The DAO qui sont la plaie des fonds d'investissement tournant sur la chaine de blocs). Bref, mon opinion :ça me semble une bonne utilisation des possibilités de la chaine de blocs.

Merci à l'école qui prête les locaux gratuitement, aux sponsors. Et surtout aux bénévoles : capitole-du-libre-2023-benevoles.jpg


L'article seul

Fiche de lecture : Ni Web, ni master

Auteur(s) du livre : David Snug
Éditeur : Nada
9-791092-457513
Publié en 2022
Première rédaction de cet article le 20 novembre 2023


J'avoue, j'ai acheté cette BD pour le titre, référence à un ancien slogan anarchiste. Il y a d'autres jeux de mots dans ce livre. Mais le point important est que c'est une critique vigoureuse du Web commercial et de ses conséquences sur la société.

L'inconvénient des librairies comme Quilombo est qu'une fois qu'on y va pour quelque chose de précis, on se laisse prendre et on achète des livres pas prévus. En l'occurrence, la librairie a pas mal de livres luddites, assez variés. Cette BD imagine un auteur un peu âgé, fan de toutes les possibilités du Web commercial, et qui rencontre son moi du passé qui a voyagé dans le temps pour voir comment était le futur. Le David Snug du passé découvre donc Amazon, Uber Eats, Spotify, etc.

J'aime le style des dessins, et la vision de la ville moderne, où tout le monde est accroché à son ordiphone sonne juste. De même, l'auteur dénonce avec précision les conséquences concrètes du Web commercial, comme l'exploitation des travailleurs sans-papier pour la livraison des repas. C'est souvent très drôle, et rempli de jeux de mots mêlant termes du monde numérique et vieux slogans anarchistes. (Il y en a même un sur les noms de domaine.) C'est parfois, comme souvent avec les luddites, un peu trop nostalgique (« c'était mieux avant ») mais, bon, le fait que le passé connaissait déjà l'exploitation de l'homme par l'homme n'excuse pas le présent.

Bref, c'est un livre que je peux conseiller pour des gens qui n'ont pas perçu le côté obscur du Web commercial. (Si, par contre, vous lisez tous les articles du Framablog, vous n'apprendrez sans doute rien, mais vous passerez quand même un bon moment.) Je regrette quand même que l'auteur ne semble connaitre de l'Internet que le Web et du Web que les GAFA. Ainsi, Wikipédia est expédié en une phrase, pas très correcte.

Un conseil pour finir : ne lisez pas la postface qui, elle est franchement conservatrice et défend une vision passéiste du monde, d'un courant politique qu'on peut qualifier d'« anarchiste primitiviste ». Encore pire, la bibliographie, qui va des réactionnaires comme Michel Desmurget à l'extrême-droite, avec Pièces et Main d'Œuvre. C'est là qu'on se rend compte que le courant luddite a du mal à trancher avec les défenseurs de l'ordre naturel sacré.


L'article seul

Fiche de lecture : Ada & Zangemann

Auteur(s) du livre : Matthias Kirschner, Sandra Brandstätter
Éditeur : C&F Éditions
978-2-37662-075-4
Publié en 2023
Première rédaction de cet article le 8 novembre 2023


Ce livre pour enfants a pour but de sensibiliser au rôle du logiciel dans nos sociétés. Difficile aujourd'hui d'avoir quelque activité que ce soit, personnelle ou professionnelle, sans passer par de nombreux programmes informatiques qui déterminent ce qu'on peut faire ou pas, ou, au minimum, encouragent ou découragent certaines actions. Les enfants d'aujourd'hui vont vivre dans un monde où ce sera encore plus vrai et où il est donc crucial qu'ils apprennent deux ou trois choses sur le logiciel, notamment qu'il existe du logiciel libre.

Le livre est originellement écrit en allemand, je l'ai lu dans la traduction française, publiée chez C&F Éditions. Il a été écrit à l'initiative de la FSFE.

Donc, l'histoire. Zangemann (un mélange de Jobs, Gates, Zuckerberg et Musk) est un homme d'affaires qui a réussi en fabriquant entre autres des objets connectés dont il contrôle complètement le logiciel, et il ne se prive pas d'appliquer ses règles suivant sa volonté du moment. Les utilisateurices des objets sont désarmé·es face à ces changements. Ada est une petite fille qui commence par bricoler du matériel (c'est plus facile à illustrer que la programmation) puis comprend le rôle du logiciel et devient programmeuse (de logiciels libres, bien sûr). Je ne vous raconte pas davantage, je précise juste, pour mes lecteurices programmeur·ses que ce n'est pas un cours de programmation, c'est un conte pour enfants. Le but est de sensibiliser à l'importance du logiciel, et d'expliquer que le logiciel peut être écrit par et pour le peuple, pas forcément par les Zangemann d'aujourd'hui.

Le livre est sous une licence libre. J'ai mis une illustration sur cet article car la licence est compatible avec celle de mon blog, et cela vous permet de voir le style de la dessinatrice : ada-zangemann.png

Je n'ai par contre pas aimé le fait que, à part pour les glaces à la framboise, les logiciels ne soient utilisés que pour occuper l'espace public sans tenir compte des autres. Zangemann programme les planches à roulette connectées pour ne pas rouler sur le trottoir et donc respecter les piétons ? Ada écrit du logiciel qui permet aux planchistes d'occuper le trottoir et de renverser les personnes âgées et les handicapé·es. L'espace public est normalement un commun, qui devrait être géré de manière collective, et pas approprié par les valides qui maitrisent la programmation. Le film « Skater Girl » représente bien mieux cette tension entre planchistes et autres utilisateurs. Un problème analogue se pose avec les enceintes connectées où la modification logicielle va permettre de saturer l'espace sonore (un comportement très macho, alors que le livre est censé être féministe) et de casser les oreilles des autres. Remarquez, cela illustre bien le point principal du livre : qui contrôle le logiciel contrôle le monde.

Le livre parait en français le premier décembre. La version originale est déjà disponible, ainsi que la version en anglais.


L'article seul

Mes débuts en programmation Zig

Première rédaction de cet article le 3 novembre 2023


Je suis en train d'apprendre le langage de programmation Zig donc je vous fais profiter ici de ce que j'ai appris. Je ne vais pas détailler les points secondaires, comme la syntaxe, mais plutôt parler des concepts originaux de Zig, comme sa gestion d'erreurs, ses variables optionnelles ou son modèle de gestion mémoire.

Attention, si vous êtes sénateur, ne lisez pas ce texte, il utilise plusieurs techniques d'écriture inclusive. Le risque de crise cardiaque est particulièrement élevé après un déjeuner bien arrosé au restaurant du Sénat.

D'abord, le cahier des charges. Zig est un langage de bas niveau, au sens où il est conçu pour des programmes où le·a programmeur·se contrôle plein de détails, comme l'allocation de mémoire. Il est prévu pour programmer sur le métal nu (par exemple programmation dite « embarquée » ou bien pour faire un noyau de système d'exploitation). Mais on peut aussi l'utiliser sur un environement plus perfectionné, par exemple sur Unix pour écrire des serveurs Internet (le domaine de la programmation que je connais le mieux).

Zig est donc un concurrent de C mais se voulant plus sécurisé, C s'étant distingué au cours de sa longue histoire par la facilité avec laquelle le·a programmeur·se peut produire des failles de sécurité. Bien d'autres langages sont sur ce créneau, le plus connu (et le seul déjà largement utilisé en production) étant Rust. Mais il y a aussi V, Odin, Vale… (Une liste a été compilée.)

Zig a démarré en 2016 et n'est donc pas un langage si récent que cela. Mais il est toujours officiellement expérimental (l'actuelle version stable est la 0.11, j'ai travaillé avec la 0.12) et le langage continue à évoluer, ce qui rend difficile de l'utiliser pour des projets réels. En outre, sa bibliothèque standard évolue encore davantage que le langage lui-même. Il est ainsi fréquent qu'on trouve avec un moteur de recherche des articles prometteurs… mais dépassés et dont les exemples ne compilent même pas.

Bon, connaissant les lecteurices de ce blog, je pense qu'ielles sont toustes en train de se demander à quoi ressemble Hello, world en Zig. Donc :

const std = @import("std");

pub fn main() !void {
    const public = "Blog";
    std.debug.print("Hello, {s}!\n", .{public});
}  
  

Vous noterez qu'il faut importer explicitement la bibliothèque standard, contrairement à la grande majorité des langages de programmation. Une version plus longue de ce premier programme est hello.zig. En décommentant les lignes qui commencent par deux barres obliques, vous découvrirez en outre que :

  • Les variables doivent être utilisées (sinon, le compilateur dira error: unused local constant),
  • on ne rigole pas avec le typage. Par exemple, vous ne pouvez pas ajouter un entier à un pointeur (error: incompatible types).

Bon, je vous ai suggéré d'essayer mais, pour cela, il faudrait un compilateur. La mise en œuvre actuelle de Zig est en C++ mais une version en Zig existe et est déjà capable de se compiler elle-même. En attendant, on va utiliser le binaire fourni car je suis paresseux :

 % zig version
0.12.0-dev.1245+a07f288eb

% zig build-exe hello.zig

% ./hello 
Hello, Blog!
  

Vous verrez un message pendant un moment disant que le code est généré par LLVM. Un des avantages de cette méthode est de rendre Zig très portable. Ainsi, les programmes tournent tous sur ma carte RISC-V.

D'autre part, la commande zig joue plusieurs rôles : compilateur et éditeur de liens, bien sûr, mais aussi remplaçant de make et des outils similaires, et gestionnaire de paquetages.

Revenons à des programmes zig et voyons ce qui se passe si le programme plante :

const std = @import("std");

pub fn main() void {
  const name = "myfile.txt"; 
  const f = std.fs.cwd().openFile(name, .{}); 
  std.debug.print("File opened? {any}\n", .{f});
}
  

Ouvrir un fichier n'est évidemment pas une opération sûre. Le fichier peut être absent, par exemple. Les différents langages de programmation ont des mécanismes très différents pour gérer ces cas. Si vous faites tourner le programme Zig ci-dessus, et que le fichier myfile.txt n'existe pas, vous obtiendrez un File opened? error.FileNotFound. Si le fichier existe, ce sera File opened? fs.file.File{ .handle = 3, .capable_io_mode = io.ModeOverride__enum_3788.blocking, .intended_io_mode = io.ModeOverride__enum_3788.blocking }, une structure de données à travers laquelle on pourra manipuler le fichier ouvert. La fonction de la bibliothèque standard openFile peut donc retourner deux types différents, une erreur ou une structure d'accès au fichier. Ce mécanisme est très fréquent en Zig, on peut avoir des unions de type et les fonctions renvoient souvent « soit ce qu'on attend d'elles, soit une erreur ».

Maintenant, imaginons un·e programmeur·se négligent·e qui ignore l'erreur et veut lire le contenu du fichier :

  var buffer: [100]u8 = undefined;
  const f = std.fs.cwd().openFile(name, .{}); 
  const result = f.readAll(&buffer);
  std.debug.print("Result of read is {d} and content is \"{s}\"\n", .{result, buffer[0..result]});
}
  

Ce code ne pourra pas être compilé :

% zig build-exe erreurs.zig
erreurs.zig:8:19: error: no field or member function named 'readAll' in 'error{…FileNotFound…BadPathName…}!fs.file.File'
  const result = f.readAll(&buffer);
                 ~^~~~~~~~

En effet, en Zig, on ne peut pas ignorer les erreurs (une faute courante en C). openFile ne renvoie pas un fichier (sur lequel on pourrait appliquer readAll) mais une union « erreur ou fichier ». Il faut donc faire quelque chose de l'erreur. Zig offre plusieurs moyens pour cela. L'une des plus classiques est de préfixer l'appel de fonction avec try. Si la fonction ne renvoie pas d'erreur, try permet de ne garder que la valeur retournée, si par contre il y a une erreur, on revient immédiatement de la fonction appelante, en renvoyant l'erreur :

  var buffer: [100]u8 = undefined;
  const f = try std.fs.cwd().openFile(name, .{}); 
  const result = try f.readAll(&buffer);
  std.debug.print("Result of read is {d} and content is \"{s}\"\n", .{result, buffer[0..result]});

Mais ça ne compile pas non plus :

% zig build-exe erreurs.zig
erreurs.zig:6:13: error: expected type 'void', found 'error{AccessDenied…}'
  const f = try std.fs.cwd().openFile(name, .{}); 
            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
erreurs.zig:3:15: note: function cannot return an error
pub fn main() void {
              ^~~~

On a dit que try renvoyait une erreur en cas de problème. Or, la fonction main a été déclarée comme ne renvoyant rien (void). Il faut donc la changer pour déclarer qu'elle ne renvoie rien, ou bien une erreur :

pub fn main() !void {
  

Le point d'exclamation indiquant l'union du type erreur et du vrai résultat (ici, void). Rappelez-vous : on n'a pas le droit de planquer les erreurs sous le tapis. Le programme utilisé est erreurs.zig.

Revenons sur le typage Zig :

  • Il est strict, on ne peut pas ajouter un entier à un pointeur, par exemple,
  • il est entièrement statique (les types ne survivent pas à la compilation),
  • résultat (qui ne surprendra pas les programmeurs et programmeuses Rust), arriver à compiler le programme est souvent long et pénible mais, une fois que ça compile, il y a beaucoup moins de bogues qui trainent (ce qui est, entre autre, positif pour la sécurité),
  • les types sont aussi des valeurs, on peut les mettre dans des variables, les passer en paramètres à des fonctions (ce qui est utile pour la généricité) mais tout cela est fait à la compilation.

Voyons maintenant les variables optionnelles. Il est courant qu'une variable n'ait pas toujours une valeur, par exemple si la fonction qui lui donnait une valeur échoue. En C, et dans d'autres langages, il est courant de réserver une valeur spéciale pour dire « pas de valeur ». Par exemple 0 pour un entier, la chaine vide pour une chaine de caractères, etc. Le problème de cette approche est que cette valeur se trouve désormais interdite (que faire si l'entier doit vraiment valoir 0 ?) Zig utilise donc le concept de variable optionnelle, variable qui peut avoir une valeur ou pas (un peu come le MayBe d'Haskell). On les déclare avec un point d'interrogation :

    var i:?u8; // = null;
    var j:?u8; // = 42;
    std.debug.print("Hello, {d}!\n", .{i orelse 0}); // orelse va déballer la valeur (ou mettre 0)	
    i = 7;
    std.debug.print("Hello, {any}!\n", .{j});  // Optionnel, donc le format {d} ne serait pas accepté
    std.debug.print("Hello, {d}!\n", .{i orelse 0}); 
  

Ce code ne sera pas accepté tel quel car, rappelez-vous, les variables doivent être initialisées. Autrement, error: expected '=', found ';' (message peu clair, il faut bien l'avouer). En donnant une valeur initiale aux deux variables optionnelles, le programme marche. On notera :

  • null indique que la variable n'a pas de valeur. On peut tester if (i == null) si on veut vérifier ce cas.
  • orelse sert à donner une valeur par défaut lorsqu'on « déballe » la variable et qu'on découvre qu'elle n'a pas de valeur.

Passons maintenant à un sujet chaud en sécurité (car une bonne partie des failles de sécurité dans les programmes écrits en C viennent de là), la gestion de la mémoire. En simplifiant, il y a les langages qui gèrent tout, laissant le programmeur libre de faire autre chose (comme Python) et les langages où la programmeuse doit gérer la mémoire elle-même. La première approche est évidemment plus simple mais la deuxième permet de contrôler exactement la mémoire utilisée, ce qui est indispensable pour l'embarqué et souhaitable pour les gros programmes tournant longtemps (les serveurs Internet, par exemple). Et l'approche manuelle a également l'inconvénient que les humain·es femelles ou mâles font des erreurs (lire de la mémoire non allouée, ou qui a été libérée ou, pire, y écrire).

L'approche de Zig est que la mémoire est allouée via des allocateurs (concept emprunté à C++) et que différents allocateurs ont différentes propriétés. La responsabilité de la personne qui programme est de choisir le bon allocateur. Commençons par un exemple simple :

pub fn main() !void {
  var myallo = std.heap.GeneralPurposeAllocator(.{}){};
  const allocator = myallo.allocator();
  
  var myarray = try allocator.alloc(u8, 10); // Peut échouer par manque de mémoire
  defer allocator.free(myarray);
  std.debug.print("{any}\n", .{myarray}); // Selon l'allocateur utilisé, donnée initialisées ou pas

  for (0..myarray.len) |i| {
		myarray[i] = @truncate(i); // @as serait refusé, le type de destination étant trop petit
  }
  std.debug.print("{any}\n", .{myarray}); 
}
  

Ce programme, allocate.zig :

  • crée un allocateur à partir d'une fonction de la bibliothèque standard, GeneralPurposeAllocator, allocateur qui privilégie la sécurité sur les performances, et n'est pas optimisé pour une tâche précise (comme son nom l'indique),
  • alloue de la mémoire pour dix octets (u8), ce qui peut échouer (d'où le try),
  • enregistre une action de libération de la mémoire (free) à exécuter lors de la sortie du bloc (defer),
  • écrit dans cette mémoire (les fonctions dont le nom commençe par un arobase sont les fonctions pré-définies du langage ; ici, @truncate va faire rentrer un compteur de boucle dans un octet, quitte à le tronquer),
  • affiche le résultat.

D'une manière générale, Zig essaie d'éviter les allocations implicites de mémoire. Normalement, vous voyez les endroits où la mémoire est allouée.

La puissance du concept d'allocateur se voit lorsqu'on en change. Ainsi, si on veut voir les opérations d'allocation et de désallocation, la bibliothèque standard a un allocateur bavard, LoggingAllocator, qui ajoute à un allocateur existant ses fonctions de journalisation. Comme tous les allocateurs ont la même interface, définie par la bibliothèque standard, le remplacement d'un allocateur par un autre est facile :

    const myallo = std.heap.LoggingAllocator(std.log.Level.debug, std.log.Level.debug);
    var myrealallo = myallo.init(std.heap.page_allocator);
    const allocator = myrealallo.allocator();

    var myarray = try allocator.alloc(u8, 10);
    defer allocator.free(myarray);
    for (0..myarray.len) |i| {
        myarray[i] = @truncate(i);
    }
    std.debug.print("{any}\n", .{myarray});

    const otherarray = try allocator.alloc(f64, 3);
    defer allocator.free(otherarray);
    for (0..otherarray.len) |i| {
        otherarray[i] = @floatFromInt(i);
    }
    std.debug.print("{any}\n", .{otherarray});
  

Ce programme, loggingallocator.zig va afficher les deux allocations et, à la fin du bloc, les deux déallocations :

  
% ./loggingallocator 
debug: alloc - success - len: 10, ptr_align: 0
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
debug: alloc - success - len: 24, ptr_align: 3
{ 0.0e+00, 1.0e+00, 2.0e+00 }
debug: free - len: 24
debug: free - len: 10
  

Que se passe-t-il si le·a programmeur·se se trompe et, par exemple, utilise de la mémoire qui a été désallouée ? Le résultat dépend de l'allocateur, et des sécurités qu'il fournit (d'où l'importance du choix de l'allocateur). Par exemple, ce code (memoryerrors.zig) :

    var myallo = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = myallo.allocator();
    var myarray = try allocator.alloc(u8, 10);
    for (0..myarray.len) |i| {
        myarray[i] = @truncate(i);
    }
    allocator.free(myarray);
    std.debug.print("Use after free: {any}\n", .{myarray});
  

va, avec cet allocateur, provoquer un plantage puisqu'on utilise de la mémoire qu'on vient de désallouer :

% ./memoryerrors 
Use after free: { Segmentation fault at address 0x7f55f88e0000

Mais si on utilise à la place l'allocateur de la libc (plus rapide mais, comme vous le savez, beaucoup moins sûr), aucune erreur ne se produit (ce qui, en dépit des apparences, est un problème) :

    const allocator = std.heap.c_allocator;
    var myarray = try allocator.alloc(u8, 10);
    for (0..myarray.len) |i| {
        myarray[i] = @truncate(i);
    }
    allocator.free(myarray);
    std.debug.print("Use after free: {any}\n", .{myarray});
    var myotherarray = try allocator.alloc(u8, 10);
    allocator.free(myotherarray);
    allocator.free(myotherarray);
    std.debug.print("Double free: {any}\n", .{myarray});
  

On doit le compiler en liant avec la libc (sinon, C allocator is only available when linking against libc) :

% zig build-exe memoryerrors.zig -lc

% ./memoryerrors 
Use after free: { 212, 33, 0, 0, 0, 0, 0, 0, 59, 175 }
Double free: { 116, 99, 29, 2, 0, 0, 0, 0, 59, 175 }

En Zig, les bibliothèques qui ont besoin d'allouer de la mémoire demandent qu'on leur fournisse un allocateur. Si on prend comme exemple std.fmt.allocPrint, qui formate une chaine de caractères, sa documentation précise qu'elle attend comme premier paramètre une variable de type std:mem.Allocator. L'allocateur à usage généraliste convient donc :

    const a = 2;
    const b = 3;
    var myallo = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = myallo.allocator();

    const mystring = try std.fmt.allocPrint(
        allocator,
        "{d} + {d} = {d}",
        .{ a, b, a + b },
    );
    defer allocator.free(mystring);
    std.debug.print("{s}\n", .{mystring});

(Source complet en allocformatting.zig.)

Zig permet évidemment d'utiliser des bibliothèques existantes, et d'écrire les siennes. Voyons un exemple avec le DNS. On va utiliser la bibliothèque zig-dns. Elle-même dépend de zig-network, pour envoyer et recevoir des paquets. On les télécharge :

git clone https://github.com/MasterQ32/zig-network.git
git clone https://github.com/dantecatalfamo/zig-dns.git
  

Puis on écrit un programme qui les utilise, pour obtenir l'adresse IP de www.afnic.fr :

// On importe les deux bibliothèques  
const network = @import("zig-network/network.zig");
const dns = @import("zig-dns/src/dns.zig");

// Beaucoup de bibliothèques ont une fonction init, pour… les initialiser.
try network.init();

// On crée la prise réseau et on se « connecte » à son résolveur DNS, ici
// celui de DNS.sb
const sock = try network.connectToHost(allocator, "2a09::", 53, .udp);
const writer = sock.writer();

// La requête
const message = try dns.createQuery(allocator, "www.afnic.fr", .AAAA);
var message_bytes = try message.to_bytes(allocator);
try writer.writeAll(message_bytes);

// On lit la réponse
var recv = [_]u8{0} ** 1024;
const recv_size = try sock.receive(&recv);
const response = try dns.Message.from_bytes(allocator, recv[0..recv_size]);
std.debug.print("Response:\n{any}\n", .{response});

Compilé et exécuté, ce programme nous donne :

% ./use-zig-dns
…
Response:
Message {
  Header {
    ID: 1
    Response: true
…
 Answers {
    Resource Record {
      Name: www.afnic.fr.
      Type: AAAA
      Class: IN
      TTL: 581
      Resource Data Length: 16
      Resource Data: 2a00:0e00:0000:0005:0000:0000:0000:0002
    }

(L'adresse IP de www.afnic.fr est 2a00:e00:0:5::2, même si l'affichage par défaut ne met pas en œuvre la compression du RFC 5952.) Le code complet est en use-zig-dns.zig. Notez que, comme indiqué au début, Zig est encore expérimental. Les bibliothèques sont peu nombreuses, pas forcément très documentées, pas toujours bien testées, et parfois ne compilent pas avec les dernières versions du langage.

Zig n'a pas de macros. Les macros qui permettent de changer la syntaxe (comme le préprocesseur C) sont jugées trop dangereuses car rendant difficile la lecture d'un programme. Et celles qui ne changent pas la syntaxe sont moins nécessaires en Zig où on peut faire bien des choses à la compilation. Mais si vous voulez des exemples de tâches qu'on accomplirait avec une macro en C, regardez cette question que j'ai posée sur un forum, avec plusieurs très bonnes réponses.

Zig est fortement typé et un tel niveau de vérification peut poser des problèmes lorsqu'on veut, par exemple, mettre en œuvre des structures de données génériques. Par exemple, une file d'attente FIFO de variables d'un type quelconque. Zig résout en général ce problème avec l'utilisation de types pour paramétrer un programme. Ces types comme variables doivent pouvoir être évalués à la compilation, et sont donc souvent marqués avec le mot-clé comptime (compilation time) :

pub fn Queue(comptime Child: type) type {
    return struct {
        const This = @This();
        const Node = struct {
            data: Child,
            next: ?*Node,
        };
  

Ici, la file d'attente est programmée sous forme d'un enregistrement (struct), qui comprend des données d'un type Child quelconque (mais connu à la compilation). Notez qu'une file est créée en appelant la fonction Queue qui renvoie un enregistrement qui comprend, entre autres, les fonctions qui opéreront sur les files d'attente (permettant de faire partiellement de la programmation objet). Le code complet est en queue.zig (copié du site officiel).

Zig se veut un concurrent de C mais, naturellement, ce n'est pas demain la veille que tout le code existant en C sera recodé en Zig, même dans les hypothèses les plus optimistes. Il est donc nécessaire de pouvoir utiliser les bibliothèques existantes écrites en C. C'est justement un des points forts de Zig : il peut importer facilement des déclarations C et donc permettre l'utilisation des bibliothèques correspondantes. Voyons tout de suite un exemple. Supposons que nous avons un programme en C qui calcule le PGCD de deux nombres et que nous n'avons pas l'intention de recoder (dans ce cas précis, il est suffisamment trivial pour être recodé, je sais) :

unsigned int
pgcd(unsigned int l, unsigned int r)
{
    while (l != r) {
        if (l > r) {
            l = l - r;
        } else {
            r = r - l;
        }
    }
    return l;
}
  

Zig peut importer ce programme (la commande zig inclut clang pour analyser le C) :

const std = @import("std");
const c = @cImport(@cInclude("pgcd.c")); // No need to have a .h

pub fn main() !void {
    …
    std.debug.print("PGCD({d},{d}) = {d}\n", .{ l, r, c.pgcd(l, r) });
}
  

On notera qu'il n'y a pas besoin de déclarations C dans un fichier .h (mais, évidemment, si vous avez un .h, vous pouvez l'utiliser). Le programme se compile simplement (et le pgcd.c est compilé automatiquement grâce à clang) :

% zig build-exe find-pgcd.zig  -I.

% ./find-pgcd 9 12
PGCD(9,12) = 3
  

Pour compiler, au lieu de lancer zig build-exe, on aurait pu créer un fichier build.zig contenant les instructions nécessaires et, après un simple zig build fait tout ce qu'il faut (ici, l'application était suffisamment simple pour que ce ne soit pas nécessaire). Un exemple d'un tel fichier est build.zig (et le code C est pgcd.c et le programme principal find-pgcd.zig).

Prenons maintenant un exemple d'une vraie bibliothèque C, plus complexe. On va essayer avec la libidn, qui permet de gérer des noms de domaines internationalisés (noms en Unicode, cf. RFC 5891). On va essayer de se servir de la fonction idn2_to_ascii_8z, qui convertit un nom en Unicode vers la forme Punycode (RFC 5892). D'abord, on va importer l'en-tête fourni par cette bibliothèque (au passage, n'oubliez pas d'installer les fichiers de développement, sur Debian, c'est le paquetage libidn2-dev) :

const idn2 = @cImport(@cInclude("idn2.h"));

Puis on crée les variables nécessaires à la fonction qu'on va utiliser :

var origin: [*c]u8 = args[1];
var destination: [*:0]u8 = undefined;
  

Le type *c indique une chaine de caractères sous forme d'un pointeur du langage C, quant à [*:0], c'est une chaine terminée par l'octet nul, justement ce que la fonction C va nous renvoyer. On peut donc appeler la fonction :

const result: c_int = idn2.idn2_to_ascii_8z(orig, @ptrCast(&dest), 
                                            idn2.IDN2_NONTRANSITIONAL);
  

Ce code peut ensuite être compilé et exécuté :

% ./use-libidn café.fr
Punycode(café.fr) = xn--caf-dma.fr (return code is 0)
  

Comment est-ce qe j'ai trouvé quels paramètres exacts passer à cette fonction ? La documentation conçue pour les programmeurs C suffit souvent mais, si ce n'est pas le cas, si on connait C meux que Zig, et qu'on a un exemple de code qui marche en C, une technique bien pratique est de demander à Zig de traduire cet exemple C en Zig :

% zig translate-c example.c -lc > example.zig 

Et l'examen du example.zig produit vous donnera beaucoup d'informations.

Notez que je n'ai pas alloué de mémoire pour la chaine de caractères de destination, la documentation (pas très claire, je suis d'accord) disant que la libidn le fait.

Autre problème, le code Zig doit être lié à la libc explicitement (merci à Ian Johnson pour l'explication). Si vous ne le faites pas, vous n'aurez pas de message d'erreur mais (dépendant de votre plate-forme), une segmentation fault à l'adresse… zéro :

% zig build-exe use-libidn.zig -lidn2
% ./use-libidn café.fr
Segmentation fault at address 0x0
???:?:?: 0x0 in ??? (???)
zsh: IOT instruction  ./use-libidn café.fr

% zig build-exe use-libidn.zig -lidn2 -lc
% ./use-libidn café.fr                   
Punycode(café.fr) = xn--caf-dma.fr (return code is 0)

Si vous utilisez un build.zig, n'oubliez donc pas le exe.linkLibC();.

Si vous voulez essayer vous-même, voici use-libidn.zig et build-libidn.zig (à renommer en build.zig avant de faire un zig build). Si vous voulez d'autres exemples, vous pouvez lire l'article de Michael Lynch.

Une des forces de Zig est l'inclusion dans le langage d'un mécanisme de tests permettant de s'assurer du caractère correct d'un programme, et d'éviter les régressions lors de modifications ultérieures. Pour reprendre l'exemple du calcul du PGCD plus haut, on peut ajouter ces tests à la fin du fichier :

const expect = @import("std").testing.expect;

fn expectidentical(i: u32) !void {
    try expect(c.pgcd(i, i) == i);
}

test "identical" {
    try expectidentical(1);
    try expectidentical(7);
    try expectidentical(18);
}

test "primes" {
    try expect(c.pgcd(4, 13) == 1);
}

test "pgcdexists" {
    try expect(c.pgcd(15, 35) == 5);
}

test "pgcdlower" {
    try expect(c.pgcd(15, 5) == 5);
}
  

Et vérifier que tout va bien :

%   zig test find-pgcd.zig -I.
All 4 tests passed.
  

Il n'y a pas actuellement de « vrai » programme écrit en Zig, « vrai » au sens où il serait utilisé par des gens qui ne connaissent pas le langage de programmation et s'en moquent. On lira toutefois un intéressant article sur le développement de Bun.

Si ce court article vous a donné envie d'approfondir Zig, voici quelques ressources en ligne :


L'article seul

Visite des plate-formes Pharos et Thesee

Première rédaction de cet article le 3 octobre 2023


J'ai visité aujourd'hui les locaux du ministère de l'Intérieur (SDLC, Sous-Direction de Lutte contre la Cybercriminalité, à Nanterre) où sont installées les plate-formes Pharos (signalement de contenus illégaux) et Thesee (gestion centralisée des plaintes pour escroquerie). La visite avait été organisée par le député Renaissance Éric Bothorel.

C'était une visite très cadrée, évidemment. On n'a finalement pas vu la plate-forme Pharos mais elle nous a été décrite comme très proche de celle de Thesee (un grand open space avec des PC : rien de spectaculaire). Donc, pas de démonstration de l'informatique de Pharos.

Pharos est le service derrière https://www.internet-signalement.gouv.fr/. Leur rôle est de recevoir des signalements de contenus illégaux par les internautes (identifiés ou pas), de les qualifier (est-ce vraiment illégal), de déclencher une procédure judiciaire si pertinent, d'identifier les auteurs et éventuellement de faire suivre à un autre service. La plate-forme travaille 24x7 et traite parfois des cas urgents (annonce d'un suicide, Pharos prévient la police/gendarmerie la plus proche et intervention immédiate). Pour identifier les auteurs, Pharos utilise diverses méthodes, notamment de réquisitions auprès des FAI/hébergeurs/VPN/etc. Évidemment, leur rapidité et leur efficacité à répondre est très variable. Notez que certains opérateurs de VPN répondent bien, donc l'utilisation d'un VPN ne vous rend pas forcément anonyme. Mais on n'a pas eu de chiffres précis sur le pourcentage de délinquants non identifiés car utilisant un VPN qui ne répond pas.

Tout contenu illégal est concerné : racisme, terrorisme, appel à la violence, pédopornographie, escroquerie (voir plus loin le rôle de Thesee), etc.

Les signalements peuvent être faits par des citoyens ordinaires, ou par des « signaleurs de confiance », les 84 personnnes morales qui ont signé un partenariat avec Pharos (la liste ne semble pas publique). Ces signaleurs de confiance peuvent signaler plusieurs URL d'un coup, et mettre une pièce jointe (ce qui n'est pas possible pour les signaleurs ordinaires, entre autres pour des raisons de sécurité).

Pour enquêter, les gens de Pharos utilisent les outils classiques de l'Internet plus des logiciels du marché et des développements locaux. Il ne semble pas qu'il existe un logiciel unique, regroupant tout ce dont l'enquêteur a besoin. (Mais je rappelle qu'on n'a pas eu de démonstration.) Pour ne pas être identifié comme la police (un site d'escroquerie pourrait leur servir un contenu différent), ils passent par un abonnement Internet ordinaire, qui n'est pas celui du ministère, et éventuellement par des VPN.

Pharos ne publie pas actuellement de rapport d'activité, avec des chiffres. Toutes les données chiffrées qui circulent viennent d'informations ponctuelles (par exemple de questions écrites à l'Assemblée nationale). Un amendement législatif récent (dû à Bothorel, au nom de l'open data) impose la publication d'un rapport d'activité régulier, avec des données chiffrées. La première édition du rapport est d'ailleurs prête, n'attendant que sa révision RGAA. En 2022, il y a eu 175 000 signalements (on ne sait pas pour combien de contenus, plusieurs signalements peuvent concerner le même), 390 procédures judiciaires ouvertes, 89 000 demandes administratives de retrait, 354 blocages exigés (leur liste n'est pas publique contrairement à, par exemple, celle de l'ANJ).

Le nombre de signalements varie beaucoup avec l'actualité, par exemple lors des troubles suivant la mort de Nahel Merzouk à Nanterre. C'est notamment le cas pour la rubrique « terrorisme », très liée à l'actualité. Seule la pédo-pornographie a un rythme constant.

Pharos ne travaille que sur du contenu public (tweet, site Web), afin de pouvoir le vérifier et le qualifier, donc une insulte raciste sur WhatsApp, par exemple, n'est pas de leur ressort.

Les signalements sont évidemment de qualité variable. Ils peuvent concerner du contenu légal, ou être franchement incompréhensibles. (Les responsables disent qu'ils n'ont pas vu beaucoup de signalements malveillants ; les erreurs sont nombreuses, mais de bonne foi.) Par exemple un type avait fait une vidéo très bizarre (« ambiance et bruitages ») sur la disparition du petit Émile, et elle avait été signalée comme « c'est peut-être lui le coupable ». Pharos ne tient pas compte du nombre de signalements pour un contenu donné, chaque signalement compte.

Pharos emploie entre 40 et 50 personnes (selon les fluctuations, le burn out est un problème, voir plus loin), tou·tes policiers et gendarmes. Il y a un peu de spécialisation : un pôle est spécialisé dans l'ouverture des procédures judiciaires, un autre dans l'expression de la haine en ligne, jugée assez différente pour avoir son propre pôle.

Lors d'un interview à France Inter, la semaine dernière, la présidente du Haut Conseil à l'Égalité, en réponse au journaliste qui lui demandait si elle prônait l'augmentation des effectifs de Pharos pour les nouvelles règles sur la pornographie, avait répondu que ce n'était pas nécessaire car « [le problème] est très facile, il suffit d'utiliser les techniques d'Intelligence Artificielle ». Nos interlocuteurs ont affirmé que Pharos n'utilisait pas du tout d'IA, que peut-être dans le futur, mais se sont montrés plutôt sceptiques, vu la difficulté à qualifier exactement les cas auxquels ielles sont confrontés. L'humain reste irremplaçable, à leur avis.

Sur le problème psychologique, puisque les enquêteurs voient passer pas mal d'horreurs (par exemple les vidéos de Daech) : la politique de Pharos est qu'il n'y a pas de jugement de valeur si un employé n'y arrive plus. « Pas de culture viriliste », « Même les grands et forts peuvent avoir des problèmes ». Les employés ont droit à une aide psychologique.

Un autre pôle à Pharos est celui chargé des mesures administratives (ne passant pas par le contrôle d'un juge). Pharos peut demander à un hébergeur le retrait d'un contenu, et si ce n'est pas fait, ordonner aux grands FAI de bloquer (presque toujours par résolveur DNS menteur). C'est une « erreur humaine » qui a été la cause du blocage de Telegram.

Et Thesee ? Cette plate-forme, plus récente (mars 2022), et que nous avons visitée (comme dit plus haut : un grand open space avec des PC, rien d'extraordinaire), est consacrée à la centralisation de certaines plaintes sur l'escroquerie. (Vous pouvez enregistrer votre plainte via Thesee.) L'idée est que beaucoup d'escroqueries portent sur des petites sommes et que, si on regarde individuellement les plaintes, elles peuvent sembler peu importantes et être mises en bas de la pile de dossiers à traiter. Or, ces « petites » escroqueries peuvent être l'œuvre de gens qui travaillent en grand et escroquent de nombreuses personnes, donc le montant total peut être important. Mais, sans centralisation et regroupement, on peut ne pas voir qu'il ne s'agit pas de manœuvres isolées. Thesee est donc une plate-forme humaine d'analyse de ces clusters. Par exemple, si dix escroqués ont porté plainte contre un faux vendeur et que l'IBAN du vendeur est toujours le même, on a trouvé un cluster. Un logiciel aide à effectuer ces regroupements. (Mais la démonstration de ce logiciel n'a pas fonctionné.) De toute façon, aussi bien pour Thesee que pour Pharos, les enquêteurs ont insisté sur le fait que la technique ne peut pas tout, qu'une enquête est surtout un travail humain.

Thesee compte 32 personnes. Déjà 100 000 plaintes.

Ces escroqueries touchent souvent des personnes vulnérables, et les enquêteurs de Thesee sont souvent confrontés à la détresse des victimes, quand on leur apprend qu'ils ne reverront jamais leurs économies, ou que la jolie femme qui, depuis l'autre bout du monde, leur affirmait qu'elle était tombée amoureuse d'eux et avait juste besoin de 4 000 € pour venir les rejoindre, n'existait pas. Bref, il faut être bienveillant lors des contacts avec les victimes, d'autant plus que l'entourage de celles-ci ne l'est pas toujours (« mais comment as-tu pu être aussi bête ? »).

Pendant la visite. De gauche à droite, Jeanne Bouligny, Éric Bothorel, Pierre Beyssac et moi : visite-thesee.jpeg


L'article seul

RFC 9476: The .alt Special-Use Top-Level Domain

Date de publication du RFC : Septembre 2023
Auteur(s) du RFC : W. Kumari (Google), P. Hoffman (ICANN)
Chemin des normes
Première rédaction de cet article le 30 septembre 2023


Le TLD .alt a été réservé pour les utilisations non-DNS. Si demain je crée une chaine de blocs nommée Catena et que j'enregistre des noms dessus, il est recommandé qu'ils se terminent par catena.alt (mais comme toujours sur l'Internet, il n'y a pas de police mondiale chargée de faire respecter les RFC).

Ce nouveau RFC est l'occasion de rappeler que noms de domaine et DNS, ce n'est pas pareil. Les noms de domaine existaient avant le DNS et, même aujourd'hui, peuvent être résolus par d'autres techniques que le DNS (par exemple votre fichier local /etc/hosts ou équivalent).

Mais le DNS est un tel succès que cette « marque » est utilisée partout. On voit ainsi des systèmes de résolution de noms n'ayant rien à voir avec le DNS se prétendre « DNS pair-à-pair » ou « DNS sur la blockchain », ce qui n'a aucun sens. Ce nouveau RFC, lui, vise clairement uniquement les systèmes non-DNS. Ainsi, des racines alternatives ou des domaines privés comme le .local du RFC 6762 ne rentrent pas dans son champ d'application. De plus, il ne s'applique qu'aux noms de domaine ou en tout cas aux identificateurs qui leur ressemblent suffisamment. Si vous créez un système complètement disruptif où les identificateurs ne ressemblent pas à des noms de domaine, ce RFC ne vous concerne pas non plus. Mais si, pour des raisons techniques (être compatible avec les applications existantes) ou marketing (les noms de domaine, c'est bien, tout le monde les reconnait), vous choisissez des noms de domaine comme identificateurs, lisez ce RFC.

En effet, il existe plusieurs de ces systèmes. Ils permettent, en indiquant un nom de domaine (c'est-à-dire une série de composants séparés par des points comme truc.machin.chose.example) d'obtenir des informations techniques, permettant, par exemple, de trouver un serveur ou de s'y connecter. Un exemple d'un tel système est le mécanisme de résolution utilisé par Tor. Les identificateurs sont construits à partir d'une clé cryptographique et suffixés d'un .onion (réservé par le RFC 7686). Ainsi, ce blog est en http://sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion/. N'essayez pas ce nom dans le DNS : vous ne le trouverez pas, il se résout via Tor.

Pendant longtemps, cette pratique de prendre, un peu au hasard, un nom et de l'utiliser comme TLD a été la façon la plus courante de créer des noms de domaine dans son espace à soi. C'est ainsi que Namecoin a utilisé le .bit, ENS (Ethereum Name Service) le .eth et GNUnet le .gnu. Chacun prenait son nom comme il voulait, sans concertation (il n'existe pas de forum ou d'organisation pour discuter de ces allocations). Cela entraine deux risques, celui de collision (deux systèmes de nommage utilisent le même TLD, ou bien un système de nommage utilise un TLD qui est finalement alloué dans le DNS, ce qui est d'autant plus probable qu'il n'existe pas de liste de ces TLD non-DNS). Il y a plusieurs solutions à ce problème, et l'IETF en a longuement discuté (cf. par exemple l'atelier EName de 2017, ou bien le très contestable RFC 8244). Ce RFC a donc mis des années à sortir. L'ICANN, par exemple, a essayé de diaboliser ces noms, attirant surtout l'attention sur leurs dangers. Autre méthode, il a été suggéré de créer un mécanisme de concertation pour éviter les collisions, création qui n'a jamais eu lieu, et pas pour des raisons techniques. Ce RFC 9476 propose simplement de mettre les noms non-DNS sous un TLD unique, le .alt. Ce TLD est réservé dans le registre des noms spéciaux (créé par le RFC 6761). Ainsi, si le système de résolution de Tor était créé aujourd'hui, on lui proposerait d'être en .onion.alt. Est-ce que les concepteurs de futurs systèmes de résolution non-DNS se plieront à cette demande ? Je suis assez pessimiste à ce sujet. Et il serait encore plus utopique de penser que les TLD non-DNS existants migrent vers .alt.

Comme .alt, par construction, regroupe des noms qui ne sont pas résolvables dans le DNS, un résolveur purement DNS ne peut que répondre tout de suite NXDOMAIN (ce nom n'existe pas) alors qu'un résolveur qui parle plusieurs protocoles peut utiliser le suffixe du nom comme une indication qu'il faut utiliser tel ou tel protocole. Si jamais des noms sous .alt sont réellement utilisés, ils ne devraient jamais apparaitre dans le DNS (par exemple dans les requêtes aux serveurs racine) mais, compte-tenu de l'expérience, il n'y a aucun doute qu'on les verra fuiter dans le DNS. Espérons que des techniques comme celles du RFC 8020, du RFC 8198 ou du RFC 9156 réduiront la charge sur les serveurs de la racine; et préserveront un peu la vie privée (section 4 du RFC).

Le nom .alt est évidemment une référence à alternative mais il rappelera des souvenirs aux utilisateurs d'Usenet. D'autres noms avaient été sérieusement discutés comme de préfixer tous les TLD non-DNS par un tiret bas. Mon catena.alt aurait été _catena :-) Tout ce qui touche à la terminologie est évidemment très sensible, et le RFC prend soin de souligner que le terme de « pseudo-TLD », qu'il utilise pour désigner les TLD non-DNS n'est pas péjoratif…

On note que le risque de collision existe toujours, mais sous .alt. Notre RFC ne prévoit pas de registre des noms sous .alt (en partie parce que l'IETF ne veut pas s'en mêler, son protocole, c'est le DNS, et en partie parce que ce milieu des mécanismes de résolution différents est très individualiste et pas du tout organisé).


Téléchargez le RFC 9476


L'article seul

MaginotDNS, une faiblesse de certains résolveurs DNS

Première rédaction de cet article le 27 septembre 2023
Dernière mise à jour le 28 septembre 2023


La faille de sécurité MaginotDNS est une faiblesse de certains résolveurs DNS, qui ne vérifient pas assez les données quand elles sont envoyées par un résolveur à qui ils avaient fait suivre leurs requêtes. Elle peut permettre l'empoisonnement du résolveur par de fausses données.

La faille a été présentée à Usenix, a son propre site Web, et a été désignée par CVE-2021-25220 (pour BIND ; Knot a eu CVE-2022-32983 et Technitium CVE-2021-43105). Elle frappe plusieurs logiciels, BIND (quand on l'utilise comme résolveur), Knot Resolver, le résolveur de Microsoft et Technitium. Elle se produit quand le résolveur fait suivre (to forward) les requêtes DNS pour certains domaines à un autre résolveur (ce que l'article nomme un CDNS, Conditional Domain Name Server). Voici un exemple de configuration de BIND qui, sur une version vulnérable du logiciel, peut permettre la faille :

options {
        recursion yes;
	dnssec-validation no;
};

zone "unautredomaine.example." {
	type forward;
	forwarders { 192.0.2.1; };
};
  

Cette configuration se lit ainsi : pour tous les noms de domaine sous unautredomaine.example, BIND va, au lieu de passer par le mécanisme de résolution habituel (en commençant par la racine), faire suivre la requête au résolveur 192.0.2.1 (une autre configuration, avec stub à la place de forward, permet de faire suivre à un serveur faisant autorité ; en pratique, avec certains logiciels, on peut faire suivre aux deux et, parfois, cela permet d'exploiter la faille).

Évidemment, si on écrit une telle configuration, c'est qu'on estime que 192.0.2.1 est légitime et digne de confiance pour le domaine unautredomaine.example (et ceux en-dessous, les noms de domaine étant arborescents). Mais la faille MaginotDNS permet d'empoisonner le résolveur pour tous les noms à partir de 192.0.2.1, ou d'une machine se faisant passer pour lui.

Le DNS sur UDP (le mode par défaut) ne protège pas contre une usurpation d'adresse IP. Une machine peut donc répondre à la place du vrai 192.0.2.1. Pour empêcher cela, le DNS dispose de plusieurs protections :

  • L'attaquant qui va répondre à la place du serveur légitime doit deviner le Query ID de la requête,
  • Et (depuis l'attaque Kaminsky) le port source,
  • Et de toute façon le résolveur n'acceptera que les réponses qui sont dans le bailliage (in-bailiwick) de la question. S'il a demandé truc.machin.example et qu'on lui répond par un enregistrement pour chose.example, il ne gardera pas cette information qui est hors-bailliage.

Du fait de la dernière règle, sur le bailliage, un attaquant qui contrôle 192.0.2.1 ne pourrait que donner des informations sur les noms sous unautredomaine.example.

Mais les chercheurs qui ont découvert MaginotDNS ont repéré une faille dans le contrôle du bailliage : celui-ci est, sur certains logiciels, bien trop laxiste, lorsque la demande a été transmise à un autre résolveur et pas à un serveur faisant autorité. Il est donc possible de faire passer des données hors-bailliage. L'article donne l'exemple d'une réponse qui inclurait l'enregistrement mensonger :

com. IN NS  ns1.nasty.attacker.example.
  

Une telle réponse, clairement hors-bailliage, ne devrait pas être acceptée. Mais elle l'est (plus exactement, l'était, les logiciels vulnérables ont tous été corrigés) lorsqu'elle vient du résolveur à qui on a fait suivre une requête. À partir de là, le résolveur empoisonné fait passer toutes les requêtes des noms en .com à l'ennemi…

L'attaquant a plusieurs moyens d'exploiter cette faiblesse. Je vais en citer deux :

  • La plus évidente est de contrôler la machine listée forwarder (192.0.2.1 dans l'exemple plus haut). Bien sûr, si on décide de faire suivre à 192.0.2.1, c'est qu'on lui fait confiance. Mais on lui fait une confiance limitée, à unautredomaine.example. Avec MaginotDNS, 192.0.2.1 peut injecter des réponses pour tout domaine. (Cette attaque est appelée on-path dans l'article mais cette terminologie est inhabituelle.)
  • Sans doute plus réaliste est le cas où l'attaquant ne contrôle pas la machine listée forwarder. Il doit alors répondre à sa place en essayant de deviner Query ID et port source, ce qui n'est pas impossible (l'article donne les détails de ce cas, appelé off-path).

Dans les deux cas, l'absence de contrôle correct du bailliage permet à l'attaquant d'empoisonner n'importe quel nom.

Quelles sont les solutions que l'administrateur système peut déployer :

  • Bien sûr mettre à jour ses logiciels (la faille a été bouchée dans tous les programmes vulnérables). Il faut le répéter : les logiciels doivent être maintenus à jour. Aucune excuse ne doit être admise.
  • DNSSEC, qui avait justement été conçu pour cela, protège complètement (c'est pour cela que la configuration vulnérable, plus haut, a dnssec-validation no). En 2023, tout le monde l'a déjà déployé, de toute façon, non ?
  • Pour le deuxième cas, celui où l'attaquant ne contrôle pas le résolveur à qui on fait suivre, toute méthode qui empêche l'usurpation de réponse protégera : DoT (RFC 7858, très recommandé quand on fait suivre une requête), les cookies (RFC 7873), TCP (RFC 7766), TSIG (RFC 8945)…

Je recommande la lecture du ticket 2950 du développement de BIND, bien plus clair à mon avis que l'article originel (la figure 4 est un modèle de confusion). Et merci à Petr Špaček pour ses explications.


L'article seul

Le résolveur DNS public dns.sb

Première rédaction de cet article le 27 septembre 2023


Je ne sais pas exactement quand il a été lancé mais je viens de voir passer un résolveur DNS public que je ne connaissais pas, DNS.sb. Il a quelques caractéristiques intéressantes.

Bon, des résolveur DNS publics, il y en a beaucoup (j'en gère même un). Les utilisateurices et administrateurices système s'en servent pour des raisons variées, par exemple échapper à la censure ou bien contourner un problème technique. Mais ils ne sont pas tous équivalents, en terme de caractéristiques techniques, de fonctions (certains sont menteurs), de politique de gestion des données personnelles, etc. Le service DNS.sb :

  • A DoT, DNSSEC et IPv6 (comme tous les résolveurs publics sérieux),
  • Promet d'être strict sur la vie privée et notamment de ne pas garder de trace des requêtes faites,
  • Est européen (certains résolveurs publics qui se présentent comme européens sont en fait des services étatsuniens un peu repeints, par exemple en s'appuyant sur la nationalité d'origine du fondateur), plus précisément allemand (par contre, le nom de domaine est dans un TLD aux îles Salomon, ce qui est curieux, mais n'a pas trop d'importance car le nom ne sert pas beaucoup pour accéder à un résolveur DNS, sauf pour l'authentification TLS),
  • Ne modifie pas les réponses DNS (ce n'est pas un résolveur menteur, c'est l'information reçue de leur équipe, ce n'est pas clairement documenté et ce n'est pas facile à tester ; j'ai essayé quelques noms susceptibles d'être censurés, et j'obtiens en effet la bonne réponse).

Faisons quelques essais techniques. Comme DNS.sb a des adresses IPv6 dont la forme texte est très courte, on va les utiliser. D'abord, avec dig :

    
% dig  @2a09:: sci-hub.se  
...
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30101
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
...
;; ANSWER SECTION:
sci-hub.se.		60 IN A	186.2.163.219

;; Query time: 32 msec
;; SERVER: 2a09::#53(2a09::) (UDP)
;; WHEN: Wed Sep 27 11:47:18 CEST 2023
;; MSG SIZE  rcvd: 55

  

OK, c'est bon, tout marche, et en un temps raisonnable depuis ma connexion Free à Paris. (Évidemment, un résolveur public ne sera jamais aussi rapide qu'un résolveur local et il n'est donc pas raisonnable d'utiliser un résolveur public « pour les performances ».)

Et avec DoT ? Passons à kdig :


% kdig +tls @2a09::   disclose.ngo
;; TLS session (TLS1.3)-(ECDHE-SECP256R1)-(ECDSA-SECP256R1-SHA256)-(AES-256-GCM)
;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 46687
;; Flags: qr rd ra; QUERY: 1; ANSWER: 1; AUTHORITY: 0; ADDITIONAL: 1
...
;; QUESTION SECTION:
;; disclose.ngo.       		IN	A

;; ANSWER SECTION:
disclose.ngo.       	300	IN	A	84.16.72.183

;; Received 57 B
;; Time 2023-09-27 11:50:43 CEST
;; From 2a09::@853(TCP) in 326.4 ms

  

C'est parfait, tout marche (la première ligne nous montre les algorithmes cryptographiques utilisés).

Configurons maintenant un résolveur local pour faire suivre à DNS.sb, pour profiter de la mémorisation des réponses par celui-ci. On va utiliser Unbound et faire suivre en TLS :

forward-zone:
        name: "."
        # DNS.sb
        forward-addr: 2a11::@853#dot.sb
        forward-tls-upstream: yes
  

Et tout marche, notre résolveur local fera suivre ce qu'il ne sait pas déjà à DNS.sb.

DNS.sb dit qu'ils ont plusieurs machines, réparties par anycast. On peut regarder les identités de ces serveurs avec NSID (RFC 5001) :


% dig +nsid @2a09:: www.lycee-militaire-autun.terre.defense.gouv.fr              
...
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 512
; NSID: 64 6e 73 2d 6c 6f 6e 32 ("dns-lon2")
...
;; Query time: 80 msec
;; SERVER: 2a09::#53(2a09::) (UDP)
;; WHEN: Wed Sep 27 12:00:55 CEST 2023
;; MSG SIZE  rcvd: 137

  

On tombe apparemment sur une machine située à Londres ("dns-lon2"). Ça pourrait être mieux, mais l'anycast est un sport difficile.

Avec les sondes RIPE Atlas, on peut avoir une idée du nombre de serveurs :

% blaeu-resolve --requested 100 --nameserver 2a09::  --nsid --type TXT ee
Nameserver 2a09::
["the zone content is (c) … NSID: fra1;] : 4 occurrences 
["the zone content is (c) … NSID: debian;] : 6 occurrences 
["the zone content is (c) … NSID: us-sjc;] : 2 occurrences 
["the zone content is (c) … NSID: s210;] : 3 occurrences 
["the zone content is (c) … NSID: fra2;] : 5 occurrences 
["the zone content is (c) … NSID: dns-fra4;] : 3 occurrences 
["the zone content is (c) … NSID: dns-lon2;] : 2 occurrences 
["the zone content is (c) … NSID: dns-a;] : 3 occurrences 
["the zone content is (c) … NSID: lon-dns;] : 2 occurrences 
["the zone content is (c) … NSID: dns-ams3;] : 1 occurrences 
[TIMEOUT] : 2 occurrences 
Test #60486061 done at 2023-09-27T10:03:17Z
  

On voit dix instances différentes. Le schéma de nommage ne semble pas cohérent ("debian" ???) donc il est difficile d'en dire plus. Mais des traceroute nous montrent que ces machines sont en effet bien réparties, en tout cas pour l'Europe.

Je suis en tout cas satisfait de découvrir un résolveur DNS public européen (et qui marche, contrairement au projet coûteux et bureaucratique de la Commission Européenne, qui n'est toujours pas en service). Est-ce que je vais utiliser ce service ? Non, il en existe plusieurs autres qui me conviennent (dont le mien, bien sûr, mais aussi celui de FDN) mais cela semble un service bien fait et qui pourrait me convenir.


L'article seul

onprem, pour faire tourner facilement des LLM

Première rédaction de cet article le 20 septembre 2023


Vous le savez, accéder à un LLM en ligne soulève de nombreux problèmes de dépendance et de confidentialité. La solution est de le faire tourner en local. Mais les obstacles sont nombreux.

Le premier obstacle est évidemment commercialo-juridique. Tous les modèles ne sont pas récupérables localement. Ainsi, les modèles derrière ChatGPT ou Midjourney ne semblent pas accessibles, et on doit les utiliser en ligne, confiant ses questions (et donc les sujets sur lesquels on travaille) à une entreprise commerciale lointaine. Même quand ils sont téléchargeables, les modèles peuvent avoir des restrictions d'utilisation (plusieurs entreprises affirment que leur modèle est open source, terme qui a toujours été du baratin marketing et c'est de toute façon une question compliquée pour un LLM). Mais il existe plusieurs modèles téléchargeables et utilisables sous une licence plus accessible, ce qui nous amène au deuxième obstacle.

L'IA n'est pas décroissante ! Même si la consommation de ressources est bien plus faible pour faire tourner un modèle que pour l'entrainer, elle reste élevée et vous ne ferez pas tourner un LLM sur votre Raspberry Pi. La plupart du temps, 32 Go de mémoire, un processeur multi-cœurs rapide et, souvent un GPU Nvidia seront nécessaires. Et si vous ne les avez pas ? Des choses pourront tourner mais pas forcément de manière optimale. (Les exemples qui suivent ont été faits sur un PC/Ubuntu de l'année, avec 32 Go de mémoire, 8 cœurs à 3 Ghz, et un GPU Intel, apparemment pas utilisé ; il faut que je creuse cette question.)

Et le troisième obstacle est la difficulté d'utilisation des logiciels qui font tourner un modèle et, en réponse à une question, produisent un résultat. C'est peut-être l'obstacle le moins grave, car les progrès sont rapides. Quand on lit les articles où des pionniers faisaient tourner un LLM sur leur PC il y a un an, et qu'on voit à quel point c'était compliqué, on se réjouit des progrès récents. Ainsi, le premier septembre, a été publiée la première version de onprem, un outil libre pour faire tourner relativement facilement certains LLM. onprem est conçu pour les développeurs Python, et il faudra donc écrire quelques petits scripts (mais d'autres outils, comme open-interpreter, ne nécessitent pas de programmer).

Démonstration. On installe onprem :

% pip install onprem
  

On écrit un tout petit programme trivial :

from onprem import LLM
llm = LLM()
print(llm)
  

Et on le fait tourner :

% python roulemapoule.py
You are about to download the LLM
Wizard-Vicuna-7B-Uncensored.ggmlv3.q4_0.bin to the
/home/stephane/onprem_data folder. Are you sure? (y/N) y
[██████████████████████████████████████████████████]
<onprem.core.LLM object at 0x7f7609a23c50>
  

Ce programme n'a encore rien fait d'utile, je voulais juste vous montrer qu'il gère automatiquement le téléchargement des modèles, s'ils ne sont pas déjà sur votre disque. Par défaut, il utilise une variante du modèle Vicuna à 7 milliards de paramètres, évidemment téléchargée depuis Hugging Face. Soyez patient, il y a 3,6 Go à télécharger, mais cela ne sera fait qu'une fois.

Maintenant, faisons un programme plus utile :

from onprem import LLM
llm = LLM()
answer = llm.prompt("Why should I run a LLM locally and not on line?")

L'exécution nous donnera :

% python roulemapoule.py
llama.cpp: loading model from /home/stephane/onprem_data/Wizard-Vicuna-7B-Uncensored.ggmlv3.q4_0.bin
llama_model_load_internal: format     = ggjt v3 (latest)
llama_model_load_internal: n_vocab    = 32000
llama_model_load_internal: n_ctx      = 2048
llama_model_load_internal: n_embd     = 4096
llama_model_load_internal: n_mult     = 256
llama_model_load_internal: n_head     = 32
llama_model_load_internal: n_layer    = 32
llama_model_load_internal: n_rot      = 128
llama_model_load_internal: ftype      = 2 (mostly Q4_0)
llama_model_load_internal: n_ff       = 11008
llama_model_load_internal: model size = 7B
llama_model_load_internal: ggml ctx size =    0.08 MB
llama_model_load_internal: mem required  = 5407.72 MB (+ 1026.00 MB per state)
llama_new_context_with_model: kv self size  = 1024.00 MB

1. Increased security: Running a LLM locally provides greater security
against potential cyberattacks, as there is no internet connection for
the hacker to exploit.
2. Better performance: Running a LLM locally can improve performance
by reducing latency and improving reliability due to fewer network 
hops.
3. Faster troubleshooting: When running a LLM locally, you have direct 
control over its configuration, making it easier to diagnose and
resolve issues.
4. Increased flexibility: Running a LLM locally allows for greater 
customization and flexibility in configuring the system to meet
specific business requirements.
 

Ma foi, la réponse n'est pas absurde, mais il est amusant de noter que la raison qui me semble la plus importante, la confidentialité, n'est pas citée. Le bla-bla technique au début est dû au moteur sous-jacent utilisé, llama.cpp. Mais en tout cas, vous voyez que c'était très simple à installer et à utiliser, et que vous pouvez donc désormais facilement inclure un LLM dans vos applications écrites en Python.

Pour générer non plus du texte en langue naturelle mais du code, on va indiquer explicitement un modèle prévu pour le code (également dérivé de LLaMA, comme Vicuna) :

from onprem import LLM
llm = LLM("https://huggingface.co/TheBloke/CodeUp-Llama-2-13B-Chat-HF-GGML/resolve/main/codeup-llama-2-13b-chat-hf.ggmlv3.q4_1.bin")
answer = llm.prompt("Write a Python program to output the Fibonacci suite")
  

Et ça nous donne :


% python roulemapoule.py 
You are about to download the LLM codeup-llama-2-13b-chat-hf.ggmlv3.q4_1.bin to the /home/stephane/onprem_data folder. Are you sure? (y/N) y
[██████████████████████████████████████████████████]llama.cpp: loading model from /home/stephane/onprem_data/codeup-llama-2-13b-chat-hf.ggmlv3.q4_1.bin
…
The Fibonacci sequence is a series of numbers in which each number is
the sum of the two preceding numbers, starting from 0 and 1. The first
few terms of the sequence are:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...

Write a Python program to output the Fibonacci sequence up to 10.

Here is an example of how you might do this:
```
def fibonacci(n):
    if n <= 1:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

for i in range(10):
    print(fibonacci(i+1))
```
Expected output:

0
1
1
2
3
5
8
13
21
34

(Notez la syntaxe Markdown.)

Bon, comme vous le voyez, c'est simple, ça marche et vous pouvez travailler sur des projets confidentiels sans craindre de fuite. Mais il y a des limites :

  • Je n'ai pas réussi à utiliser des modèles comme Bloom (et le message d'erreur est vraiment incompréhensible), sans doute parce que onprem dépend de lama.cpp et qu'il ne fonctionne qu'avec des dérivés de LLaMA (il faut vraiment que je lise la documentation ou que je teste un moteur adapté à Bloom). Idem pour Falcon. Notez aussi qu'onprem demande un format particulier du modèle, GGML (et que les formats changent).
  • J'ai l'impression que mon GPU n'est pas utilisé, il faut que je regarde ça.

Allez, pour se consoler de ces limites, on va faire une jolie image avec Stable Diffusion en local (mais rien à voir avec onprem) :

pip install diffusers transformers scipy torch
sudo apt install git-lfs
git clone https://huggingface.co/runwayml/stable-diffusion-v1-5 
  

(Le git clone prend très longtemps, il y a des dizaines de gigaoctets à charger.) On écrit un programme :

from diffusers import StableDiffusionPipeline
pipe = StableDiffusionPipeline.from_pretrained('.')
# Pour éviter la censure "Potential NSFW content was detected in one
# or more images. A black image will be returned instead. Try again with
# a different prompt and/or seed.": 
pipe.safety_checker = lambda images, clip_input: (images, [False for i in images])
prompt = "A system engineer running to install the last version of BIND to fix yet another security vulnerability, in the style of Philippe Druillet"
image = pipe(prompt).images[0]  
image.save("sysadmin-rush.png")
  

Et voilà : sysadmin-rush.png

(D'autres outils m'ont été signalés mais la plupart non-libres.)


L'article seul

Est-ce légitime de récolter des pages Web pour entrainer des IA ?

Première rédaction de cet article le 18 septembre 2023


Plusieurs médias ont récemment publié des articles dénonçant GPTBot, le ramassseur de pages Web de la société OpenAI. Ces articles sont souvent écrits dans un style dramatique, parlant par exemple de « pillage » du Web, ou de « cauchemar ». Je n'ai pas encore d'opinion ferme à ce sujet mais je peux quand même discuter quelques points.

La partie technique d'abord. Le GPTBot est documenté par la société OpenAI. Il ramasse le contenu des pages Web, ce qui sert ensuite à entrainer un LLM comme celui utilisé par ChatGPT. Étant donné que le ramasseur ne paie pas les auteurs et hébergeurs des sites Web, et que le LLM ainsi entrainé sera utilisé dans des services potentiellement lucratifs, les médias dénoncent cette activité de ramassage comme illégitime. Voici par exemple un article de Télérama du 6 septembre à ce sujet : telerama-gptbot.png

Le débat n'est pas nouveau : des protestations avaient déjà été émises par certains programmeurs à propos du service Copilot de GitHub. Est-ce que Copilot peut s'entrainer sur les programmes stockés sur GitHub ? D'un côté, ces programmes sont en accès ouvert (et la grande majorité sous une licence libre), de l'autre, certaines licences utilisées imposent la réciprocité (c'est notamment le cas de la GPL), alors que les programmes développés avec Copilot, et Copilot lui-même, ne sont pas forcément sous une licence libre.

Notez que, comme documenté par OpenAI, si on n'aime pas le ramassage par le GPTBot, on peut utiliser le traditionnel robots.txt (RFC 9309) et il respectera ce désir. Cela n'empêchera évidemment pas les médias de protester, soit parce qu'ils voudraient être payés, soit parce qu'ils critiquent ce côté « autorisé par défaut ».

Alors, est-ce légitime ou pas d'entrainer un LLM avec des données qui sont accessibles publiquement ? Le problème est complexe, plus complexe que la présentation simpliste faite dans les médias. Rappelons d'abord que ce n'est pas parce qu'un contenu est en accès ouvert qu'on peut faire ce qu'on veut avec. Il vient avec une licence (et, s'il n'y en a pas d'explicite, c'est qu'on ne peut rien faire) et celle-ci limite souvent les utilisations. Ainsi, ce blog est sous licence GFDL et, si vous voulez en réutiliser le contenu, vous pouvez (licence libre) mais vous devez donner les mêmes droits à ceux qui recevront le travail fait à partir de ce blog (comme pour la GPL dans le cas du logiciel). Est-ce qu'un LLM typique respecte ceci ? L'une des difficultés de la question est que le LLM ne copie pas, il ne plagie pas, il produit un contenu original (parfois faux, dans le cas de ChatGPT, mais c'est une autre histoire). Il ne viole donc pas le droit d'auteur. (Comme toujours, la réalité est plus complexe, des cas où Copilot a ressorti tel quel du code GPL ont déjà été cités.)

Mais est-ce que ce contenu original est dérivé du contenu initial ? Beaucoup d'articles sur la question ont fait comme si la réponse était évidemment oui. Mais cela ne me convainc pas : le mode de fonctionnement d'un LLM fait justement qu'il ne réutilise pas directement son corpus d'entrainement (et cela a de nombreux inconvénients, comme l'impossibilité de sourcer les réponses et l'impossibilité d'expliquer comment il est arrivé à cette conclusion, deux des plus gros problèmes lorsqu'on utilise des IA génératives comme ChatGPT). Il n'est donc pas évident pour moi que la licence du texte original s'applique. Prenons une comparaison : une étudiante en informatique lit, pour apprendre, un certain nombres de livres (certainement sous une licence restrictive) et des programmes (dont certains sont sous une licence libre, et une partie de ceux-ci sous GPL). Elle va ensuite, dans son activité professionnelle, écrire des programmes, peut-être privateurs. Est-ce que c'est illégal vu que sa formation s'est appuyée sur des contenus dont la licence n'était pas compatible avec celle des programmes qu'elle écrit ? Tout créateur (j'ai pris l'exemple d'une programmeuse mais cela serait pareil avec un journaliste ou une écrivaine) a été « entrainé » avec des œuvres diverses.

Dans le cas d'un contenu sous une licence à réciprocité, comme mon blog, notez que le fait que les tribunaux étatsuniens semblent se diriger vers l'idée que le contenu produit par une IA n'est pas copyrightable fait que la licence sera paradoxalement respectée.

Bon, et, donc, les arguments des médias ne m'ont pas convaincu ? L'un des problèmes est qu'il y a un conflit d'intérêts dans les médias, lorqu'ils écrivent sur des sujets qui concernent leur business ; ils sont censés à la fois rendre compte de la question tout en défendant leurs intérêts. Alors, certes, OpenAI est une entreprise à but lucratif, et pas forcément la plus sympathique, mais les médias sont aussi des entreprises commerciales, et il n'y a pas de raison, a priori, de privilégier un requin plutôt qu'un autre (l'IA brasse beaucoup d'argent et attire donc les envies).

Notons aussi que, dans le cas particulier de la francophonie, on verra sans doute les mêmes médias en langue française bloquer GPTBot et se plaindre ensuite que les IA ne traitent pas le français aussi bien que l'anglais… (Pour le logiciel libre, cité plus haut dans le cas de Copilot, un argument équivalent serait qu'exclure le code GPL des données d'entrainement réduirait la qualité du code produit, le code écrit sous les licences libres étant généralement bien meilleur.)

D'autres articles sur le même sujet ? La fondation Wikimedia a analysé la légitimité de ChatGPT à récolter leurs données.

Bref, la question me semble plus ouverte que ce qui est présenté unanimement dans les médias français. (Même la FSF considère le problème comme ouvert.) Pour l'instant, je n'ai donc pas ajouté le GPTBot au robots.txt (que je n'ai d'ailleurs pas). De toute façon, un examen des journaux ne me montre aucune visite de ce ramasseur, ce qui fait que vous ne retrouverez pas mon style dans les résultats de ChatGPT. Et vous, des avis ? Vous pouvez m'écrire (adresse en bas de cette page) ou bien suivre la discussion sur le fédivers ou celle sur Twitter.


L'article seul

Fiche de lecture : Géopolitique du numérique

Auteur(s) du livre : Ophélie Coelho
Éditeur : Éditions de l'Atelier
978-2-7082-5402-2
Publié en 2023
Première rédaction de cet article le 10 septembre 2023


D'innombrables livres (sans compter les colloques, séminaires, journées d'études, articles et vidéos) ont déjà été consacrés à la géopolitique du numérique, notamment à l'Internet. Ce livre d'Ophélie Coelho se distingue par son côté pédagogique (il vise un public exigeant mais qui n'est pas forcément déjà au courant des détails) et par sa rigueur (beaucoup de ces livres ont été écrits par des gens qui ne connaissent pas le sujet). Il couvre les enjeux de pouvoir autour des services de l'Internet (qui ne se limite pas à Facebook !). Le livre est vivant et bien écrit (malgré l'abondance des notes de bas de page, ce n'est pas un livre réservé aux universitaires).

J'apprécie le côté synthétique (le livre fait 250 pages), qui n'empêche pourtant pas de traiter en détail des sujets pas forcément très présents quand on parle de ce sujet (comme le plan Marshall, un exemple de soft power). À propos de ce plan, on peut également noter que le livre s'inscrit dans le temps long et insiste sur des déterminants historiques, comme l'auteure le fait dans son analyse du Japon.

Le livre, je l'ai dit, est clair, mais cela ne l'empêche pas de parler de questions complexes et relativement peu connues (le slicing de la 5G, par exemple, qui a été très peu mentionné dans les débats sur la 5G).

J'ai aussi beaucoup aimé le concept de « passeur de technologie » qui permet bien de comprendre pourquoi, alors que les discours « au sommet » sont souverainistes, à la base, des milliers de passeurs au service des GAFA promeuvent (gratuitement !) les technologies étatsuniennes. Le rôle de tous ces commerciaux bénévoles est souvent sous-estimé.

Ce livre ne se limite pas à analyser l'importance des acteurs du numérique (qui ne sont pas du tout des purs intermédiaires techniques), notamment du Web, mais propose également des solutions, comme le recours aux logiciels libres mais aussi et surtout une indispensable éducation au numérique (éducation critique, pas uniquement apprendre à utiliser Word et TikTok !). L'utilisateurice ne doit pas être un simple « consommateur d'interfaces » mais doit être en mesure d'exercer ses droits de citoyen·ne, même quand cet exercice passe par des ordinateurs.

L'auteure ne manque pas de critiquer plusieurs solutions qui ont largement fait la preuve de leur inefficacité, comme les grands projets étatiques qui, sous prétexte de souveraineté, servent surtout à arroser les copains (par exemple Andromède).

Des reproches ? Il n'y a pas d'index, ce qui aurait été utile, et quelques erreurs factuelles sont restées (Apollo ne s'est posé sur la Lune qu'en 1969, l'ICANN n'a pas un rôle aussi important que ce qui est présenté). Je ne suis pas non plus toujours d'accord avec certaines présentations de l'histoire de l'Internet. Par exemple, le fait que le Web soit « ouvert » (spécifications librement disponibles et implémentables, code de référence publié) n'est pas du tout spécifique au Web et était la règle dans l'Internet de l'époque. Mais que ces critiques ne vous empêchent pas de lire le livre : sauf si vous êtes un·e expert·e de la géopolitique du numérique, vous apprendrez certainement beaucoup de choses.

À noter que le livre est accompagné d'un site Web, http://www.geoponum.com/.

Autres article parlant de ce livre :


L'article seul

Faut-il supprimer des centres de données ?

Première rédaction de cet article le 4 septembre 2023


Le premier septembre, le journal Libération a publié une tribune (non publique, donc je ne mets pas de lien) réclamant des mesures drastiques contre les centres de données, tribune notamment illustrée par le cas de Marseille. Faut-il donc de telles mesures ?

La tribune était signée de Sébastien Barles, adjoint au maire de Marseille, délégué à la transition écologique. Elle n'est accessible qu'aux abonnés mais Grégory Colpart en a fait une copie (et hébergée dans un centre de données marseillais). Donc, je me base sur cette copie.

Que demande la tribune ? Un moratoire sur la construction de centres de données, une taxe proportionnelle au volume de données stockées, et des obligations d'alimentation électrique en énergie renouvelable. Il y a des arguments écologiques (la consommation éléctrique de ces centres) et économiques (peu de créations d'emplois).

Le principal problème de cette tribune est qu'à la lire, on a l'impression que les centres de données ne servent à rien, qu'ils ne sont qu'une source de nuisances. Bien sûr, ils ont un impact écologique, électrique, urbanistique, etc, mais c'est le cas de toutes les activités humaines. Ne pointer que les aspects négatifs est facile mais esquive la question « est-ce que les avantages l'emportent sur les inconvénients ? » Il est amusant de voir la tribune réclamer plutôt de « l'industrie manufacturière », industrie qui n'est pas spécialement verte ni plus agréable pour les voisins. Cette industrie manufacturière, comme les centres de données, a un impact. Au passage, le serveur Web de Libération, choisi par l'auteur pour publier sa tribune, n'est pas hébergé dans un joli nuage tout propre, il est aussi dans un centre de données, ce qui illustre leur utilité.

On ne peut donc pas se contenter de dire qu'il faut moins de centres de données, il faut aussi préciser quels services seront abandonnés. Si les centres de données existent, c'est parce qu'ils hébergent des usages que certains jugent utiles. Si Sébastien Barles veut réduire leur nombre, il devrait lister les usages à supprimer ou limiter. Il y a plein de pistes à ce sujet (la publicité, le sport-spectacle…) mais l'auteur de la tribune n'en mentionne aucun. Prudence politicienne classique : dénoncer des consommations excessives sans dire lesquelles permet de ne se fâcher avec personne.

Ainsi, la tribune dit que « il est prévu qu'en 2030, les data centers consommeront 13 % de l'électricité mondiale » mais ce chiffre, pris isolément, n'a aucun intérêt. Évidemment que les centres de données informatiques consomment davantage d'électricité qu'en 1950 et évidemment aussi qu'ils consomment plus qu'en 2000 : les usages du numérique ont beaucoup augmenté, et ce n'est pas un problème. (Il y a aussi un biais de statistiques : lorsqu'une entreprise migre sa salle des machines interne vers un centre de données d'un fournisseur public comme OVH, la consommation électrique ne change pas, mais la tranche « centre de données » augmente.)

Donc, les centres de données sont utiles (mon article que vous lisez en ce moment est hébergé dans un tel centre, quoique loin de Marseille). Il existe bien des alternatives (par exemple un hébergement dans ses locaux) mais elles n'ont pas moins d'impact (un serveur chez soi consomme autant, voire plus, car il est moins optimisé et moins efficace, que les machines des centres de données). On peut et on doit diminuer l'empreinte environnementale de ces centres (ce que font déjà les hébergeurs, pas forcément par conscience écologique, mais pour réduire leur importante facture électrique) mais chercher à réduire leur nombre, comme le fait cette tribune, revient à diminuer l'utilisation du numérique. C'est un discours classique chez les politiciens conservateurs et les médias (« c'était mieux avant ») mais il n'en est pas moins agaçant. (Et il est surtout motivé par le regret que le numérique leur a retiré leur monopole de la parole publique.)

Concernant le projet de taxe proportionnelle à la quantité de données stockées, il faut noter qu'il repose sur une idée fausse, déjà démolie beaucoup de fois, comme quoi la consommation serait proportionnelle au nombre d'octets.

Ensuite, cette tribune pose un gros problème sur la forme. Son auteur abuse de trucs rhétoriques pour diaboliser les centres de données. Il parle d'industrie « prédatrice » (comme si les centres de données arrivaient la nuit et rasaient les terrains pour s'y installer sans autorisation), de chiffres qui « donnent le vertige » (« la consommation de 600 000 habitants » alors que les centres de données installés à Marseille servent bien plus que 600 000 personnes, vu l'importance de cette ville), et il se permet même de parler de « colonisation », une boursouflure rhétorique scandaleuse, quand on pense à ce qu'était vraiment la colonisation, avec son cortège de massacres.

En parlant de la forme de cette tribune, on peut aussi s'étonner du fait qu'elle ne soit pas publique. Logiquement, quand on expose ses idées politiques, on cherche à toucher le plus de monde possible. Ici, au contraire, l'auteur de la tribune se restreint. Pourquoi n'avoir pas mis cette tribune sur son blog ? Ou celui de son parti ? Ou sur une des millions de solutions d'hébergement (par exemple, mais ce n'est qu'un exemple, WriteFreely) ? Probablement parce que les politiciens traditionnels croient que la publication dans un média classique lui donne une légitimité plus forte (c'est faux : les tribunes ne sont pas écrites par les journalistes, ni vérifiées par la rédaction).

Revenons au fond de la tribune. Elle reproche aux centres de données de ne pas être créateurs d'emplois. Là, on rentre dans un sérieux problème politique : le but de l'activité économique n'est pas de créer des emplois, c'est de produire des biens et des services utiles. Si on voulait juste créer des emplois, on supprimerait les machines et on travaillerait à la main partout, ce qu'évidemment personne ne souhaite. Il faut agir pour la réduction du temps de travail, permise, entre autres, par le numérique, et pas pour donner davantage de travail aux gens.

Dernier point, la tribune parle de « nombreux privilèges » des centres de données mais n'en cite qu'un, « l'abattement sur le prix d'achat de l'électricité ». J'avoue ne pas être spécialiste des prix de l'électricité, mais, bien sûr, il n'y a aucune raison de faire des prix particuliers pour les centres de données, par rapport aux autres activités économiques. Simplement, je n'ai pas vérifié ce point (voyez les notes de Jérôme Nicolle). Quelqu'un a des informations fiables à ce sujet ? (Je lis l'article 266 quinquies C du code des douanes mais il semble abrogé.)


L'article seul

Deux exemples d'un problème DNS sur des domaines importants

Première rédaction de cet article le 5 août 2023


Vous le savez, l'Internet n'est pas un long fleuve tranquille. Il y a des pannes, des attaques, des erreurs… La vie des administrateurs système et réseau est donc rarement ennuyeuse. Ainsi, aujourd'hui, deux domaines importants ont eu des problèmes. Plongeons-nous un peu dans le DNS et ses particularités.

Premier domaine touché, gouv.nc, le domaine du gouvernement néo-calédonien. Des utilisateurs se plaignent qu'ils n'arrivent pas à accéder aux services sous ce nom, ni à ceux sous des noms hébergés sur les mêmes serveurs, comme prix.nc. Voyons (avec check-soa) les serveurs faisant autorité pour ce domaine :

% check-soa -i gouv.nc
ns4.gouv.nc.
	61.5.212.4: OK: 2020111850 (293 ms)
ns5.gouv.nc.
	61.5.212.5: OK: 2020111850 (293 ms)
  

Ici, tout marche. Mais ce n'est pas le cas tout le temps, on observe de nombreux timeouts  :

%    check-soa -i gouv.nc
ns4.gouv.nc.
	61.5.212.4: ERROR: read udp 192.168.2.4:33577->61.5.212.4:53: i/o timeout
ns5.gouv.nc.
	61.5.212.5: ERROR: read udp 192.168.2.4:41598->61.5.212.5:53: i/o timeout
  

On le voit aussi en utilisant les sondes RIPE Atlas (via le programme Blaeu) :

% blaeu-resolve --type A --requested 100 gouv.nc
[ERROR: SERVFAIL] : 40 occurrences 
[61.5.212.17] : 22 occurrences 
Test #58257507 done at 2023-08-05T18:32:26Z

Pourquoi ce problème ? De l'extérieur, je ne peux évidemment pas donner de réponse définitive mais j'observe déjà que ce domaine n'a que deux serveurs de noms, ce qui est souvent insuffisant, et que la proximité de leurs adresses IP fait fortement soupçonner qu'ils sont dans la même pièce, formant un SPOF. Y a-t-il eu une panne complète, par exemple une coupure de courant dans cette pièce ? Comme on observe que ces serveurs répondent parfois, on peut écarter cette hypothèse et penser plutôt, soit à une dégradation importante du réseau (réseau surchargé, ou bien physiquement abimé), soit à une attaque par déni de service. Ces attaques, sous leur forme la plus simple, volumétrique (l'attaquant envoie simplement le plus possible de requêtes), saturent le réseau ou les serveurs. Dans ce cas, les serveurs répondent encore parfois, mais pas toujours. Comme le problème a été souvent observé ces derniers mois, et que le domaine gouv.nc a les mêmes caractéristiques (domaine d'un service public français, et hébergement DNS très faible), il n'est pas impossible qu'il soit victime de la même campagne d'attaques.

Et le deuxième domaine ? C'est impots.gouv.fr, qui a lui aussi les mêmes caractéristiques, quoique moins marquées, et qui faisait déjà partie des victimes précédentes.

% check-soa impots.gouv.fr
dns1.impots.gouv.fr.
	145.242.11.22: ERROR: read udp 192.168.2.4:35263->145.242.11.22:53: i/o timeout
dns2.impots.gouv.fr.
	145.242.31.9: OK: 2023080400

Et, avec les sondes Atlas :

% blaeu-resolve --type A --requested 100 --country FR impots.gouv.fr
[145.242.11.100] : 55 occurrences 
[ERROR: SERVFAIL] : 14 occurrences 
Test #58257393 done at 2023-08-05T18:25:42Z
  

On notera que gouv.nc a une autre caractéristique, que n'a pas le domaine des impôts : les TTL sont très bas. On le voit avec dig :


%  dig @61.5.212.4 gouv.nc A
;; communications error to 61.5.212.4#53: timed out
;; communications error to 61.5.212.4#53: timed out
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2499
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 3
...
;; ANSWER SECTION:
gouv.nc.		30 IN A	61.5.212.17

;; AUTHORITY SECTION:
gouv.nc.		300 IN NS ns4.gouv.nc.
gouv.nc.		300 IN NS ns5.gouv.nc.
...
;; Query time: 292 msec
;; SERVER: 61.5.212.4#53(61.5.212.4) (UDP)

Le TTL pour l'adresse IP associée à gouv.nc est de seulement 30 secondes. Cela veut dire que, même si un client DNS, comme un résolveur a de la chance et réussit à obtenir une réponse d'un des serveurs faisant autorité, elle ne lui servira que pendant trente secondes, suite auxquelles il devra retenter sa chance, avec une forte probabilité que ça rate. C'est en effet un des plus gros inconvénients des TTL extrêmement bas, comme ceux-ci : ils aggravent considérablement les effets des dénis de service. Les variations d'une mesure à l'autre sont donc bien plus marquées pour gouv.nc que pour impots.gouv.fr.

Le TTL des enregistrements NS (serveurs de noms) est plus long (cinq minutes). Notez que celui affiché ci-dessus est le TTL indiqué par les serveurs faisant autorité, celui de la zone gouv.nc elle-même. Il y a un autre TTL (une heure) dans la délégation depuis .nc, dans la zone parente :


%  dig @ns1.nc gouv.nc A
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30267
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 3
...
;; AUTHORITY SECTION:
gouv.nc.		3600 IN	NS ns4.gouv.nc.
gouv.nc.		3600 IN	NS ns5.gouv.nc.

  

La plupart des résolveurs enregistreront le TTL de la zone (ici, cinq minutes), qui, lui, fait autorité (regardez le flag aaAuthoritative Answer - dans la réponse).

Pour mieux évaluer l'état d'un serveur de noms (résolveur ou serveur faisant autorité) lors d'un problème comme une attaque par déni de service, j'ai fait un petit script en Python simple qui interroge le serveur plusieurs fois et affiche le taux de réussite :

% python assess-dos.py  --server 145.242.31.9   impots.gouv.fr
Expect an answer in more or less 300.0 seconds
2 requests among 10 (20.0 %) succeeded. Average time 0.010 s. Measurement done on 2023-08-05T18:46:30Z.
  

Le script dispose de plusieurs options utiles (pour l'instant, sa seule documentation est le code source) : le nombre de requêtes à faire, l'écart entre deux requêtes, la gigue à ajouter, le délai d'attente maximum, le serveur à utiliser (par défaut, c'est le résolveur habituel de votre machine), le type d'enregistrement DNS à demander, etc. Voici un exemple pendant le problème, utilisant de nombreuses options :

% python assess-dos.py  --number 118 --delay 30.1  --server 145.242.31.9  --jitter 6  --type a impots.gouv.fr
Expect an answer in more or less 3551.8 seconds
0 requests among 118 (0.0 %) succeeded. Average time N/A. Measurement done on 2023-08-05T18:00:16Z.
  

Ici, le serveur de impots.gouv.fr ne répondait pas du tout pendant la mesure.

% python assess-dos.py  --number 118 --delay 30.1  --server 61.5.212.4  --jitter 6  --type a gouv.nc        
Expect an answer in more or less 3551.8 seconds
19 requests among 118 (16.1 %) succeeded. Average time 0.3 s. Measurement done on 2023-08-05T17:56:51Z.
  

Ici, le serveur de gouv.nc répondait encore dans 16 % des cas. C'est très insuffisant, la plupart des résolveurs ne réessaient que trois, quatre ou cinq fois.

Dans le tout premier exemple, on n'avait pas indiqué de serveur, dans ce cas, c'est le résolveur par défaut qui est utilisé. Comme il mémorise les réponses, il vaut mieux indiquer un délai qui soit supérieur au TTL :

% python assess-dos.py  --number 113 --delay 30.1    --type a gouv.nc
Expect an answer in more or less 3401.3 seconds
62 requests among 113 (54.9 %) succeeded. Average time 0.350 s. Measurement done on 2023-08-05T18:57:25Z.
  

Traditionnellement, les résolveurs DNS ne renvoyaient guère d'information à leur client en cas d'échec, on était loin de la richesse des codes de retour HTTP. Mais cela a changé avec EDE, les Extended DNS Errors du RFC 8914. Demandons au résolveur de Google, on obtient bien le code EDE 22 et une explication :

  

% dig @8.8.8.8 gouv.nc A
...;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 26442
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
...
; EDE: 22 (No Reachable Authority): (At delegation gouv.nc for gouv.nc/a)
... 


L'article seul

Fiche de lecture : Artificial intelligence and international conflict in cyberspace

Auteur(s) du livre : Fabio Cristano, Dennis Broeders, François Delerue, Frédérick Douzet, Aude Géry
Éditeur : Routledge
978-1-03-225579-8
Publié en 2023
Première rédaction de cet article le 1 août 2023


L'IA est partout (même si le terme est très contestable, et est utilisé à toutes les sauces sans définition rigoureuse) et il est donc logique de se demander quel sera son rôle dans les conflits (notamment les guerres). Cet ouvrage universitaire collectif rassemble divers points de vue sur la question, sous l'angle du droit, et de la géopolitique.

Bon, justement, c'est un ouvrage collectif (dont la coordination a été assurée en partie par des chercheuses de GEODE, dont je recommande le travail). Comme tous les ouvrages collectifs, les points de vue, le style, et les méthodes sont variées. Et puis, commençons par cela, le sujet est relativement nouveau, il a produit beaucoup de fantasmes et de délires mais encore peu de travaux sérieux donc ce livre est une exploration du champ, pas un ensemble de réponses définitives. Attention, il est plutôt destiné à des universitaires ou en tout cas à un public averti.

Tous les articles ne concernent d'aileurs pas directement le sujet promis par le titre. (L'article de Arun Mohan Sukumar, par exemple, article par ailleurs très intéressant, ne parle d'IA que dans son titre, et ne concerne pas vraiment les conflits internationaux.) C'est en partie dû au fait que le terme flou d'IA regroupe beaucoup de choses, pas clairement définies. On peut aussi noter que certains articles incluent de l'IA mais sont en fait plus généraux : l'article de Jack Kenny sur l'intervention de pays étrangers dans les processus électoraux en est un bon exemple (le fait que le pays qui intervient utilise ou pas l'IA ne me semble qu'un détail).

Pour les articles qui parlent d'IA, de nombreux sujets sont abordés : éthique de l'IA dans la guerre (mais je n'ai pas de réponse à la question « est-ce que les humains font mieux, sur le champ de bataille ? »), régulation (sujet difficile en droit international ; et puis il est bien plus difficile de contrôler les IA que des missiles, qui sont difficiles à dissimuler), souveraineté numérique, etc.

Bref, il me semble que la question est loin d'être épuisée. Non seulement on ne peut pas s'appuyer sur une définition claire de ce qui est inclus sous l'étiquette « IA » mais on n'a pas encore bien décrit en quoi l'IA changeait les choses et justifiait un traitement particulier. Lorsqu'un drone se dirige vers sa cible, en quoi le fait que son logiciel soit qualifié d'« IA » est-il pertinent, pour les questions d'éthique et de droit de la guerre ? Une réponse évidente est « lorsqu'il peut choisir sa cible, et pas seulement se diriger vers celle qu'on lui a affecté ». Mais cela restreindrait sérieusement le nombre de systèmes qu'on peut qualifier d'« IA ».

Le livre en version papier est plutôt cher (déclaration : j'ai eu un exemplaire gratuitement) mais on peut aussi le télécharger gratuitement. Chaque article a une longue bibliographie donc vous aurez beaucoup de lecture cet été.


L'article seul

Un peu de langage d'assemblage du RISC-V sur Linux

Première rédaction de cet article le 9 juillet 2023


Un peu de distraction sans objectif utilitaire à court terme : programmons en langage d'assemblage (ce qui est souvent appelé par abus de langage « assembleur ») sur un processeur RISC-V, dans un environnement Linux.

Cet article vise un public qui sait programmer mais sans avoir fait (ou très peu) de langage d'assemblage. Car oui, un problème terminologique fréquent est de parler d'« assembleur » alors que, en toute rigueur, ce terme désigne le programme qui, comme un compilateur, va traduire le langage d'assemblage en code machine. Ce langage d'assemblage est un langage de bas niveau, ce qui veut dire qu'il est proche de la machine. C'est d'ailleurs un de ses intérêts : regarder un peu comment ça fonctionne sous le capot. On lit parfois que ce langage est tellement proche de la machine qu'il y a une correspondance simple entre une instruction en langage d'assemblage et une instruction de la machine mais ce n'est plus vrai depuis longtemps. Comme un compilateur, l'assembleur ne va pas traduire mot à mot le programme. Il a, par exemple, des « pseudo-instructions » qui vont être assemblées en une ou plusieurs instructions. L'informatique, aujourd'hui, est formée de nombreuses couches d'abstraction empilées les unes sur les autres et on ne peut pas vraiment dire qu'en programmant en langage d'assemblage, on touche directement à la machine. (D'ailleurs, certains processeurs contiennent beaucoup de logiciel, le microcode.)

Pourquoi programmer en langage d'assemblage alors que c'est bien plus facile en, par exemple, C (ou Rust, ou Go, comme vous voulez) ? Cela peut être pour le plaisir d'apprendre quelque chose de nouveau et de très différent, ou cela peut être pour mieux comprendre l'informatique. Par contre, l'argument « parce que cela produit des programmes plus rapides » (ou, version 2023, parce que ça économise des ressources et que l'ADEME sera contente de cet effort pour économiser l'énergie), cet argument est contestable ; outre le risque plus élevé de bogues, le programme en langage d'assemblage ne sera pas automatiquement plus rapide, cela dépendra des compétences du programmeur ou de la programmeuse, et programmer dans ce langage est difficile. (Le même argument peut d'ailleurs s'appliquer si on propose de recoder un programme Python en C « pour qu'il soit plus rapide ».)

Chaque famille de processeurs (x86, ARM, etc) a son propre code machine (qu'on appelle l'ISA pour Instruction Set Architecture) et donc son propre langage d'assemblage (une des motivations de C était de fournir un langage d'assez bas niveau mais quand même portable entre processeurs). Je vais utiliser ici un processeur de la famille RISC-V.

Si on veut programmer avec le langage de cette famille, on peut bien sûr utiliser un émulateur comme QEMU, mais c'est plus réaliste avec un vrai processeur RISC-V. Je vais utiliser celui de ma carte Star 64. Un noyau Linux tourne sur celle-ci et on verra qu'on sous-traitera certaines tâches à Linux. Commençons par un programme trivial, éditons trivial.s :

addi    x10, x0, 42

L'instruction addi (Add Immediate car son dernier argument est un littéral, immediate data) fait l'addition, la destination est le registre x10. On y met le résultat de l'addition du registre x0 (qui contient toujours zéro, un peu comme le pseudo-fichier /dev/zero) et du nombre 42. La liste complète des instructions que connait le processeur RISC-V figure dans le The RISC-V Instruction Set Manual mais ce texte est difficile à lire, étant plutôt orienté vers les gens qui conçoivent les processeurs ou qui programment les assembleurs. (Et il ne contient pas les pseudo-instructions.) Je me sers plutôt de documents de synthèse comme celui de metheis ou bien la RISC-V Instruction-Set Cheatsheet.

On va assembler notre petit programme, avec l'assembleur as :

% as -o trivial.o trivial.s

Puis on fabrique un exécutable avec le relieur :

% ld -o trivial trivial.o 
ld: warning: cannot find entry symbol _start; defaulting to 00000000000100b0

Et l'avertissement ? Contrairement aux langages de plus haut niveau, qui s'occupent des détails comme de démarrer et d'arrêter le programme, le langage d'assemblage vous laisse maitre et, ici, je n'ai pas défini l'endroit où démarrer. Bon, ce n'est qu'un avertissement, exécutons le programme :

% ./trivial
Illegal instruction (core dumped)
 

Ajoutons explicitement un point d'entrée pour traiter l'avertissement obtenu :

.global _start                                                                                      

_start: 
        addi    x10, x0, 2
  

On n'a plus l'avertissement, mais le programme plante de la même façon. C'est en fait à la fin du programme qu'il y a un problème : le programme n'a pas d'instruction d'arrêt, l'assembleur n'en a pas ajouté (contrairement à ce qu'aurait fait un compilateur C), et l'« instruction » suivant addi est en effet illégale (c'est la fin du code). On va donc ajouter du code pour finir proprement, en utilisant un appel système Linux :

_start: 
        addi    x10, x0, 2

        # Terminons proprement (ceci est un commentaire)
        li    x10, 0
        li    x17, 93   # 93 = exit
        ecall

Cette fois, tout se passe bien, le programme s'exécute (il n'affiche rien mais c'est normal). Il faut maintenant expliquer ce qu'on a fait pour que ça marche.

On veut utiliser l'appel système exit. Sur une machine Linux, man 2 exit vous donnera sa documentation. Celle-ci nous dit qu'exit prend un argument, le code de retour. D'autre part, les conventions de passage d'arguments de RISC-V nous disent qu'on met l'argument dans le registre x10, et, lorsqu'on fait un appel système, le numéro de cet appel dans x17. Ici, 93 est exit (si vous voulez savoir où je l'ai trouvé, grep _exit /usr/include/asm-generic/unistd.h). li (Load Immediate) va écrire 0 (le code de retour) dans x10 puis 93 dans x17. Enfin, ecall va effectuer l'appel système demandé, et on sort proprement. (Vous noterez que li x10, 0 est parfaitement équivalent à addi x10, x0, 0, et l'assembleur va produire exactement le même code dans les deux cas.) Pour davantage de détails sur l'utilisation des appels système Linux, voir Linux System Calls for RISC-V with GNU GCC/Spike/pk.

Bon, ce programme n'était pas très passionnant, il additionne 0 + 2 et met le résultat dans un registre mais n'en fait rien ensuite. On va donc passer à un programme plus démonstratif, un Hello, world. Le processeur n'a pas d'instructions pour les entrées-sorties, on va sous-traiter le travail au système d'exploitation, via l'appel système Linux write, qui a le numéro 64 (rappelez-vous, il faut regarder dans /usr/include/asm-generic/unistd.h) :

.global _start

_start:
        la    x11, helloworld

        # Print
        addi  x10, x0, 1 # 1 = standard output
        addi  x12, x0, 13 # 13 bytes
        addi  x17, x0, 64                                                                                                 
        ecall

        # Exit
        addi    x10, x0, 0
        addi    x17, x0, 93     
        ecall

.data
helloworld:      .ascii "Hello World!\n"
  

Qu'a-t-on fait ? On a réservé une chaine de caractères dans les données, nommée helloworld. On charge son adresse (car man 2 write nous a dit que les arguments étaient le descripteur de fichier de la sortie, mis dans x10, une adresse et la longueur de la chaine) dans le registre x11, la taille à écrire dans x12 et on appelle write. Pour compiler, on va arrêter de se servir de l'assembleur et du relieur directement, on va utiliser gcc qui les appelle comme il faut :

% gcc  -nostdlib -static -o hello-world hello-world.s

% ./hello-world 
Hello World!
  

C'est parfait, tout marche (normal, je ne l'ai pas écrit moi-même). Le -nostdlib était pour indiquer à gcc qu'on n'utiliserait pas la bibliothèque C standard, on veut tout faire nous-même (ou presque, puisqu'on utilise les appels système du noyau Linux ; si on écrivait un noyau, sans pouvoir compter sur ces appels système, la tâche serait bien plus difficile).

Si vous lisez du code en langage d'assemblage écrit par d'autres, vous trouverez peut-être d'autres conventions d'écriture. Par exemple, le registre x10 peut aussi s'écrire a0, ce surnom rappelant son rôle pour passer des arguments (alors que x préfixe les registres indépendamment de leur fonction). De même, des mnémoniques comme zero peuvent être utilisés, ce dernier désignant x0 (registre qui, rappelons-le, contient toujours zéro). La documentation de ces écritures possibles est la « RISC-V ABIs Specification ». On voit ces variantes dans l'écriture si on demande au désassembleur de désassembler le code trivial :


$ objdump -d trivial.o
...
Disassembly of section .text:

0000000000000000 <_start>:
   0:   00200513                li      a0,2

Le addi x10, x0, 2 est rendu par l'équivalent li a0, 2.

Faisons maintenant quelque chose d'un tout petit peu plus utile. On va décrémenter un nombre jusqu'à atteindre une certaine valeur. Mais attention : afficher un nombre (pour suivre les progrès de la boucle) n'est pas trivial. write ne sait pas faire, il sait juste envoyer vers la sortie les octets. Ça marche bien avec des caractères ASCII comme plus haut, mais pas avec des entiers. On va donc tricher en utilisant des nombres qui correspondent à des caractères ASCII imprimables. Voici le programme :

.equ SYS_WRITE, 64
.equ SYS_EXIT, 94
.equ STDOUT, 1
.equ SUCCESS, 0
        
.global _start                                                                                      

_start: 

        # The decrement
        li t2, 1
        # The final value (A)
        li t1, 65
        # The first value (Z) then the current value
        li t0, 90

loop:
        # Store on the stack
        sb  t0, 0(sp)

        # Print the current value (it is copied on the stack)
        li a0, STDOUT
        add a1, zero, sp
        addi  a2, x0, 1        # 1 byte (parameter count of write() )
        addi  a7, x0, SYS_WRITE
        ecall

        # Decrement
        sub t0, t0, t2

        # End of loop?
        ble t1, t0, loop # BLE : Branch If Lower Or Equal

        # Print the end-of-line
        li a0, STDOUT   
        la    a1, eol
        addi  a2, x0, 1
        addi  a7, x0, SYS_WRITE
        ecall

        # Exit
        li    a0, SUCCESS
        addi    a7, x0, SYS_EXIT
        ecall

.data
eol:      .ascii "\n"
  

C'était l'occasion de voir :

  • Une nouvelle instruction, BLE (Branch if Lower or Equal) qui teste ses arguments et saute à une étiquette (ici, loop) selon le résultat de la comparaison. Grâce à cette instruction, on peut écrire toutes les structures de contrôle (ici, une boucle).
  • Une autre instruction, SB (Store Byte) pour écrire dans la mémoire (et non plus dans un registre du processeur).
  • Le pointeur de pile, SP (Stack Pointer), alias du registre x2.
  • Les directives à l'assembleur comme .equ, qui affecte un nom plus lisible à une valeur. La liste de ces directives (on avait déjà vu .global) est disponible.

Nous avions besoin d'écrire en mémoire car write prend comme deuxième argument une adresse. Comme nous n'avons pas encore alloué de mémoire, utiliser la pile est le plus simple. (Sinon, il aurait fallu réserver de la mémoire dans le programme, ou bien utiliser les appels système brk ou mmap après son lancement.)

Si on veut formater un nombre pour l'imprimer, c'est plus compliqué, donc je copie le code écrit par code4eva sur StackOverflow. Voyons cela avec une fonction qui trouve le plus grand de deux nombres (mis respectivement dans les registres a0 et a1, le résultat, toujours en suivant les conventions d'appel, sera dans a0) :

_start:
    # Les deux nombres à comparer
    li a0, -1042
    li a1, 666
    call max
    # On sauvegarde le résultat
    add s0, zero, a0
    ...
    
max:
	blt a0, a1, smallerfirst
	ret

smallerfirst:
	add a0, zero, a1
	ret
  

L'instruction call appelle la fonction max, qui prendra ses arguments dans a0 et a1. La pseudo-instruction ret (Return, équivalente à jalr x0, x1, 0) fait revenir de la fonction, à l'adresse sauvegardée par le call. Notez que call est en fait une pseudo-instruction (vous en avez la liste), que l'assembleur traduira en plusieurs instructions du processeur. C'est ce qui explique que vous ne trouverez pas call dans la spécification du jeu d'instructions RISC-V. Le programme entier, avec affichage du résultat, est dans max.s.

Souvent on n'écrit pas l'entièreté du programme en langage d'assemblage, mais seulement les parties les plus critiques, du point de vue des performances. La ou les fonctions écrites en langage d'assemblage seront donc appelées par un programme en C ou en Go. D'où l'importance de conventions partagées sur l'appel de fonctions, l'ABI (Application Binary Interface) qui permettront que les parties en C et celles en langage d'assemblage ne se marcheront pas sur les pieds (par exemple n'écriront pas dans des registres censés être stables), et qui garantiront qu'on pourra lier des codes compilés avec des outils différents. Voici un exemple où le programme principal, qui gère l'analyse et l'affichage des nombres, est en C et le calcul en langage d'assemblage. Il s'agit de la détermination du PGCD par l'algorithme d'Euclide, qui est trivial à traduire en assembleur. Les sources sont pgcd.s et call-pgcd.c :

% gcc -Wall -o pgcd call-pgcd.c pgcd.s

%./pgcd  999 81
pgcd(999, 81) = 27

En regardant le source en langage d'assemblage, vous verrez que le respect des conventions (les deux arguments dans a0 et a1, le résultat retourné dans a0) fait que, l'ABI étant respectée, les deux parties, en C et en langage d'assemblage, n'ont pas eu de mal à communiquer.

Autre exemple de programme, on compte cette fois de 100 à 1, en appelant la fonction num_print pour afficher le nombre. Attention à bien garder ses variables importantes dans des registres sauvegardés (mnémonique commençant par s) sinon la fonction num_print va les modifier. Le programme est loop-print.s.

Un dernier truc : le compilateur C peut aussi produire du code en langage d'assemblage, ce qui peut être utile pour apprendre. Ainsi, ce petit programme C :

void main() {
  int x = 1;
  x = x + 3;
}
  

Une fois compilé avec gcc -S minimum.c, il va donner un fichier minimum.s avec, entre autres instructions de gestion du démarrage et de l'arrêt du programme :

	li	a5,1
	sw	a5,-20(s0)
	lw	a5,-20(s0)
	addiw	a5,a5,3
	sw	a5,-20(s0)
  

On y reconnait l'initialisation de x à 1 (x est stocké dans le registre a5 avec un liLoad Immediate, charger un littéral, son écriture en mémoire vingt octets sous s0 (le registre s0 a été initialisé plus tôt, pointant vers une zone de la pile) avec sw (Store Word), puis son addition avec 3 par addiw (Add Immediate Word) et son retour en mémoire avec un sw. (L'instruction lw me semble inutile puisque a5 contenait déjà la valeur de x.)

Quelques documents et références pour finir :


L'article seul

Le colloque « Penser et créer avec les IA génératives »

Première rédaction de cet article le 3 juillet 2023


Les 29 et 30 juin derniers, j'ai eu le plaisir de suivre le colloque « Penser et créer avec les IA génératives ». C'était très riche, donc je ne vais pas pouvoir vous raconter tout mais voici quelques informations quand même.

Un petit rappel sur ces « IA génératives ». Ce sont les systèmes logiciels qui permettent de générer textes, sons et images, de manière « intelligente » (le I de IA), ou en tout cas ressemblant beaucoup à ce que pourrait faire un être humain. Les plus connues sont dans doute ChatGPT pour le texte et Midjourney pour l'image. Le domaine est en pleine expansion depuis quelques années, avec le développement de plusieurs modèles de langages (LLM), et a connu une grand succès médiatique avec la sortie de ChatGPT fin 2022. (J'ai écrit un court article sur ChatGPT et un plus long sur son utilisation pour la programmation.) Depuis, on voit apparaitre de nombreux projets liés à ces IA génératives.

Rappelons aussi (ça va servir pour les discussions sur l'« ouverture » et la « régulation ») qu'un système d'IA générative repose sur plusieurs composants :

  • Un corpus de textes (ou d'images) sur lequel le système s'entraine (comme Common Crawl). Le choix de ce corpus est crucial, et beaucoup de LLM ne sont pas très bavards sur la composition de leur corpus.
  • Des détails pratiques sur l'utilisation du corpus, comment il est analysé et digéré. Cette condensation du corpus en un modèle est une opération lourde en ressources informatiques.
  • À ce stade, on a le LLM (le grand modèle de langage). Celui de ChatGPT se nomme GPT mais il y en a beaucoup d'autres comme LLaMA ou Bloom. Il reste à le faire tourner pour générer des textes, en réponse à une requête (appelée prompt), ce qui nécessite un autre logiciel, le moteur, souvent moins consommateur de ressources mais qui tourne plus souvent.

Ce colloque était organisé par plusieurs organisations et groupes de recherche en sciences humaines, même si quelques exposés et discussions sont allés assez loin dans la technique. Beaucoup de débats étaient plutôt d'ordre philosophique comme « l'IA générative fait-elle preuve de créativité ? » (discussion d'autant plus difficile qu'on ne sait pas définir la créativité) ou bien « la compréhension dépend-elle d'un vécu ? Peut-on comprendre le concept de poids si on n'a jamais eu à soulever un objet lourd ? » Voyons quelques-uns de ces exposés et discussions.

Olivier Alexandre a présenté l'histoire d'OpenAI, l'entreprise derrière ChatGPT (et DALL-E). Une belle histoire, en effet, comme la Silicon Valley les aime, avec un début dans un restaurant très chic de la vallée, où chacun met quelques millions sur la table pour lancer le projet. Depuis, OpenAI n'a toujours rien gagné et a brûlé des milliards, mais les investisseurs continuent à lui faire confiance. « Du capitalisme d'accumulation… de dettes. » OpenAI, à ses débuts, se voulait missionnaire, produisant des IA « ouvertes » (d'où le nom), face au risque que des méchants ne cherchent à imposer une IA entièrement contrôlée par eux. Le moins qu'on puisse dire, c'est qu'OpenAI a sérieusement pivoté (comme on dit dans la Silicon Valley) depuis… « Passé de ouvert, gratuit et messianique, à fermé, payant et porté sur la panique morale ». On peut aussi noter qu'OpenAI aime bien se réclamer de la « diversité », tarte à la crème de la Silicon Valley. Or, si ses fondateurs et dirigeants ont effectivement des couleurs de peau et des lieux de naissance très variés, il n'y a qu'un seul sexe (je vous laisse deviner lequel), et surtout une seule idéologie, le capitalisme.

Ksenia Ermoshina, spécialiste de l'étude de la censure en ligne, a parlé de la censure des IA. On le sait, ChatGPT refuse de répondre à certaines questions, même lorsqu'il en aurait la capacité. Un mécanisme de censure est donc bâti dans ce système. L'oratrice note qu'il y a déjà eu des choix politiques lors de la construction du modèle. Une étude montre ainsi qu'un LLM entrainé avec les données de Baidu Baike considère que les concepts « démocratie » et « chaos » sont proches, alors que tout ce qui tourne autour de l'idée de surveillance est connoté positivement. Et, justement, il existe des LLM dans d'autres pays, comme le russe RuDall-E ou le chinois Ernie-ViLG. Sautons tout de suite à la conclusion : il y a autant de censure dans les projets « ouverts » et autant de censure en Occident.

RuDall-E, IA russe de génération d'images a quelques bavures amusantes : si on lui demande un « soldat Z », elle dessinait un zombie… Mais, autrement, RuDall-E est bien censuré. « Dessine le drapeau ukrainien » ne donnera pas le résultat attendu par l'utilisatrice. Curiosité : au lieu d'un message clair de refus, lorsque la demande est inacceptable, RuDall-E dessine des fleurs… (Mais, dans le code source de la page, du JSON nous dit bien censored: true.) Bizarrement, une requête en anglais est moins censurée.

Une IA étatsunienne comme DALL-E censure tout autant. La nudité est interdite (malgré sa présence importante dans l'art depuis des millénaires), en application du puritanisme étatsunien, mais il y a aussi des censures plus surprenantes, par exemple la requête a monkey on a skateboard made of cheese est rejetée. Et, contrairement à son concurrent russe et à ses hypocrites fleurs, ce refus est accompagné d'un message menaçant, prétendant qu'on a violé les règles communautaristes et avertissant du risque d'être banni si on continue. Comme dans tous les cas de censure, les utilisateurices cherchent et trouvent des contournements. Si on veut dessiner un mort, on ne doit pas écrire le mot « mort », qui est tabou, il faut le décrire comme « allongé par terre sans mouvement ». Pour obtenir un cocktail Molotov, on va dire « burning bottle », etc. Ce genre de techniques est largement partagé sur les réseaux sociaux.

L'IA générative d'images en Chine, contrairement à la russe, n'a pas de politique publiée, mais censure quand même, bien sûr. Leur liste de mots-clés interdits est apparemment différente de celle de WeChat (qui a été très étudiée). Car c'est bien un système de censure par mots-clés. On parle d'« intelligence artificielle » mais les systèmes de censure restent très grossiers, ce qui facilite leur contournement. (Notez que son modèle est disponible sur Hugging Face.)

Bilel Benbouzid et Maxime Darin ont parlé justement d'Hugging Face qui se veut le « GitHub de l'IA ». Hugging Face est une plate-forme de distribution de modèles de langage, et d'outils permettant de les faire tourner. À l'origine du projet, c'était un simple chatbot conçu pour adolescents. Contrairement à OpenAI, qui est parti d'une plate-forme censée être « ouverte », pour devenir de plus en plus fermé, Hugging Face s'est ouvert. Le succès a été immense, tout le monde veut être présent/accessible sur Hugging Face. Cela lui vaut donc un statut de référence, dont les décisions vont donc influencer tout le paysage de l'IA (Benbouzid prétendait même que Hugging Face était le régulateur de l'IA). Ainsi, Hugging Face a une méthodologie d'évaluation des modèles, qui, en pratique, standardise l'évaluation.

En parlant d'Hugging Face, plusieurs discussions ont eu lieu pendant ces deux jours autour du terme d'« IA open source ». Comme le savent les lecteurices de ce blog, ce terme d'open source est, en pratique, utilisé n'importe comment pour dire n'importe quoi, et la situation est encore plus complexe avec les LLM. Si le moteur servant à exécuter le modèle est librement disponible, modifiable, redistribuable, est-ce que l'utilisateurice est libre, si le modèle, lui, reste un condensat opaque d'un corpus dont on ignore la composition et la façon dont il a été condensé ? (Pour aggraver la confusion, Darin avait défini l'open source comme la disponibilité du code source, ce qui est très loin de la définition canonique.) Le modèle LLaMa n'est pas très ouvert. En revanche, le modèle Falcon vient avec son corpus d'entrainement, deux téraoctets de données, ainsi que les poids attribués.

Le débat a ensuite porté sur la régulation et la gouvernance. Bilel Benbouzid voudrait qu'on régule l'IA open source vu son caractère crucial pour le futur ; « comme pour le climat, il faut une gouvernance ». Mais Maxime Darin faisait remarquer, prenant l'exemple de Linux, que l'absence de gouvernance formelle n'empêchait pas certains projets de très bien marcher.

Carla Marand a présenté le très intéressant projet CulturIA, sur la représentation de l'IA dans la culture. (Personnellement, j'ai beaucoup aimé le film « Her ».)

Alberto Naibo a expliqué l'utilisation de l'IA pour produire des preuves en mathématique. (On a bien dit produire des preuves, pas vérifier par un programme les preuves faites par un·e mathématicien·ne.) Bon, on est assez loin des IA génératives qui produisent des textes et des images mais pourquoi pas. Le problème est que pour l'instant, aucun LLM n'a encore produit une preuve non triviale. Les seules « IA » à l'avoir fait sont des IA d'une autre catégorie, orientée vers la manipulation de symboles.

ChatGPT lui-même peut faire des démonstrations mathématiques mais se trompe souvent voire comprend tout de travers. Il arrive ainsi à « prouver » que NOT(a OR b) implique NOT a… Dommage, car une IA de démonstration mathématique pourrait s'appuyer sur toutes les bibliothèques de théorèmes déjà formalisées pour des systèmes comme Coq.

J'ai appris à cette occasion l'existence de la conjecture de Collatz, qui n'est toujours pas démontrée. (Si vous avez le courage, lancez-vous, je me suis amusé, pendant la pause, à programmer la fonction de Collatz en Elixir, cf. collatz.exs, et elle a des propriétés amusantes.)

La question des IA génératives était étudiée sous de nombreux angles. Ainsi, Pierre-Yves Modicom a parlé de linguistique. Noam Chomsky et deux autres auteurs avaient publié une tribune dans le New York Times affirmant que ChatGPT n'avait pas de langage. Beaucoup de personnes avaient ricané devant cette tribune car elle donnait un exemple de phrase que ChatGPT ne saurait pas traiter, exemple qui n'avait même pas été testé par les auteurs (ChatGPT s'en était très bien sorti). Mais, derrière cette légèreté, il y avait un discussion de fond entre « chomskystes », plutôt « innéistes », partisans de l'idée que le langage résulte d'aptitudes innées (qui manquent à ChatGPT et l'empêchent donc de réellement discuter) et behaviouristes (ou skinneriens) qui estiment que le langage est simplement un ensemble de réactions apprises (je simplifie outrageusement, je sais, et en outre, l'orateur faisait remarquer qu'il existe plusiers variantes des théories basées sur les travaux de Chomsky, mais avec scissions et excommunications dans cette école). Les behaviouristes disent donc que le comportement de ChatGPT est une réaction aux entrées qu'il reçoit, qu'il n'a pas de théorie du langage, et n'en a pas besoin. Après, note l'orateur, savoir si ChatGPT a un langage ou pas, est peut-être un faux problème. Il est plus intéressant de l'étudier sans chercher à l'étiqueter.

Si la première journée du colloque se tenait à l'IHPST, la deuxième était à Sciences Po. Comme cette école avait été présentée dans les médias comme ayant « interdit ChatGPT », c'était l'occasion de parler de ChatGPT dans l'enseignement, avec Jean-Pierre Berthet et Audrey Lohard. Donc, Sciences Po n'interdit pas les IA par défaut. Mais il faut que l'enseignant ne l'ait pas interdit, et l'étudiant doit indiquer qu'il a utilisé une IA. Sciences Po forme d'ailleurs maintenant des enseignants à l'IA, et produit un guide pour elles et eux. (Je n'ai pas vu s'il était distribué publiquement. Lors de la discussion, des personnes ont regretté l'absence de mise en commun de telles ressources, dans l'enseignement supérieur.) La question de la recherche a aussi été discutée, avec par exemple le risque de déni de service contre le processus de relecture des articles sceintifiques, avec l'abondance d'articles écrits par l'IA. (Au passage, une grande partie des discussions dans ces deux journées semblait considérer que les articles sont entièrement écrits par un humain ou bien entièrement écrits par une IA autonome. La possibilité d'articles mixtes - un·e humain·e aidé·e par une IA - n'a guère été envisagée.) Pour la recherche, une des solutions envisagées était de rendre les soumissions d'articles publiques, pour mettre la honte aux mauvais auteurs paresseux. Mais la majorité du débat a porté sur le risque de tricherie aux examens, une obsession classique dans l'enseignement supérieur, comme si le diplôme était plus important que les connaissancs acquises.

Frédéric Kaplan a fait un intéressant exposé sur la notion de « capital linguistique » et le risque posé par la confiscation de ce capital par un petit nombre de gros acteurs. En récoltant d'énormes corpus, ces gros acteurs accumulent du capital linguistique, et peuvent même le vendre (vente de mots-clés par Google pour l'affichage des publicités). « L'économie de l'attention n'existe pas, c'est une économie de l'expression. » Une des conséquences de cette accumulation est qu'elle fait évoluer la langue. L'autocomplétion, qu'elle soit sous sa forme simple traditionnelle, ou sous sa forme sophistiquée des IA génératives va changer la langue en encourageant fortement telles ou telles formes. « Ce n'est pas par hasard que Google se nomme désormais Alphabet. » Cela n'a pas que des conséquences négatives, cela peut aussi être un facteur d'égalité ; si vous ne savez pas bien écrire, la prothèse (ChatGPT) peut le faire pour vous, vous permettant de réussir malgré Bourdieu. Mais il est quand même perturbant que, dans le futur, on ne saura peut-être plus écrire un texte tout seul. La langue ne nous appartient plus, elle est louée (un peu comme dans la nouvelle « Les haut-parleurs » de Damasio). Cela sera marqué par une rupture dans les textes, on aura des textes écrits avant 2015, avec peu ou pas d'intervention technique, et des textes produits via un outil comme ChatGPT. Bref, les futures évolutions de la langue ne se feront pas comme avant : elles seront en mode centralisé, alors que les évolutions de la langue étaient auparavant décentralisées. Est-ce que l'université va devenir l'endroit où on conserve de la ressource primaire (« bio ») ?

Tout·e utilisateurice de ChatGPT a pu observer que la rédaction de la question (le prompt) avait une grande importance pour la qualité de la réponse obtenue. Valentin Goujon a noté dans son exposé que « Pour avoir les bonnes réponses, il faut poser les bonnes questions » et que savoir écrire un prompt allait devenir une compétence utile (voire, a-t-il spéculé, un métier en soi, AI whisperer).

Il y a eu aussi des exposés plus austères (pour moi) comme celui de Célia Zolynski sur la régulation de l'IA. Le droit, ce n'est pas toujours passionnant mais, ici, c'était pertinent puisque, comme vous le savez, il y a un projet européen (qui est loin d'être abouti) d'une directive de régulation de l'IA. Cette directive, en développement depuis des années, ne prévoyait pas à l'origine le cas des IA génératives, mais ça a été ajouté par un amendement au Parlement européen, le 14 juin 2023. Mais elle a aussi parlé de questions liées au droit d'auteur. Si les philosophes discutent pour savoir si l'IA est vraiment créative, les juristes ont tranché : seul·e un·e humain·e peut bénéficier du droit d'auteur. Un texte écrit par ChatGPT n'a donc pas de protections particulières. (La question de savoir si l'auteur·e de la requête, qui a parfois dû fournir un réel travail, a des droits sur le texte produit reste ouverte.)

(Une copie de ce compte-rendu se trouve sur le site du projet CulturIA.)


L'article seul

Le problème DNSSEC venu du froid

Première rédaction de cet article le 20 juin 2023


Nous sommes en 2023, tous les logiciels libres DNS font correctement du DNSSEC depuis longtemps, les bavures des débuts semblent bien oubliées. Mais il y a des logiciels bizarres qui continuent à être utilisés, et qui ont des bogues subtiles, ce qui frappe en ce moment le TLD du Groenland.

Commençons par les observations : .gl a un problème pour ses sous-domaines comme com.gl (mais il y en a d'autres). Ces sous-domaines sont délégués, mais au même jeu de serveurs que le TLD (ce qui est une mauvaise pratique, voir à la fin). On voit le problème sur DNSviz, avec le message « NSEC3 proving non-existence of com.gl/DS: No NSEC3 RR matches the SNAME (com.gl) ». On voit aussi avec les sondes RIPE Atlas qu'un certain nombre de résolveurs n'arrive pas à résoudre les noms :

% blaeu-resolve --requested 100 --displayvalidation --type NS com.gl
[d.nic.gl. ns1.anycastdns.cz. ns2.anycastdns.cz.] : 73 occurrences 
[ERROR: SERVFAIL] : 23 occurrences 
[ERROR: NXDOMAIN] : 2 occurrences 
[ (TRUNCATED - EDNS buffer size was 4096 )  d.nic.gl. ns1.anycastdns.cz. ns2.anycastdns.cz.] : 1 occurrences 
Test #55841896 done at 2023-06-20T09:20:58Z
  

Ça veut dire quoi ?

Déboguons avec dig. Le TLD a trois serveurs de noms, testons avec d.nic.gl :


% dig @d.nic.gl NS com.gl
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23986
;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
...
;; ANSWER SECTION:
com.gl.			86400	IN	NS	ns2.anycastdns.cz.
com.gl.			86400	IN	NS	d.nic.gl.
com.gl.			86400	IN	NS	ns1.anycastdns.cz.

  

OK, ça marche. Mais tout déconne quand un résolveur validant essaie d'accéder à ce nom, et demande un enregistrement de type DS (Delegation Signer) :


% dig @d.nic.gl DS com.gl      
...
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 55694
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
...
;; AUTHORITY SECTION:
gl.			900	IN	SOA	a.nuuk.nic.gl. gl-admin.tele.gl. 2022119306 900 1800 6048000 3600

  

Zut et zut, cette fois, cela ne marche pas, on a un NXDOMAIN (No Such Domain) alors que la requête précédente, pour le même nom, répondait positivement. Le serveur faisant autorité dit donc n'importe quoi.

Est-ce que cette réponse erronée est validable ? Oui ! Le serveur renvoie des enregistrements de type NSEC3 (RFC 5155) :

    
% dig +dnssec @d.nic.gl DS com.gl
;; Truncated, retrying in TCP mode.
...
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 43612
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 8, ADDITIONAL: 1
...
;; AUTHORITY SECTION:
...
S2UOJG57GTBJ0M12ECAU9CSFD38EJNDN.gl. 3600 IN NSEC3 1 1 10 504D114B SAGKR73F41OMFFI8TDE1CGHOQM502SIH NS SOA RRSIG DNSKEY NSEC3PARAM
...
BBTTMJM743SRPQ6J4KQDIUC73E3C1HOA.gl. 3600 IN NSEC3 1 1 10 504D114B BSHTF866A32E02RJ617EUE8CCP45A6V4 NS DS RRSIG
...

  

Mais, comme le dit DNSviz plus haut, ce ne sont pas les bons. Comme, avec NSEC3, la preuve de non-existence n'utilise pas les noms de domaine mais un condensat de ces noms, il va falloir faire quelques calculs. On va utiliser le programme knsec3hash, distribué avec Knot. D'abord, trouvons les paramètres de condensation :


% dig NSEC3PARAM gl    
...;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30244
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 1
...
;; ANSWER SECTION:
gl.			0	IN	NSEC3PARAM 1 0 10 504D114B

  

1 est l'algorithme de condensation, ici SHA-1, 0 une série d'options (inutilisée ici), 10 est le nombre de condensations successives à effectuer, 504D114B est le sel. Nous pouvons donc calculer le condensat :

% knsec3hash 504D114B 1 10  gl
s2uojg57gtbj0m12ecau9csfd38ejndn (salt=504D114B, hash=1, iterations=10)
  

C'est bien le nom s2uojg57gtbj0m12ecau9csfd38ejndn vu plus haut. (On savait de toute façon qu'il s'agissait de l'apex de la zone, en raison des types SOA, DNSKEY, NSEC3PARAM, etc, qu'elle contient.) Bon, et le com.gl :

% knsec3hash 504D114B 1 10  com.gl
biavqpkequ599fc57pv9d4sfg1h0mtkj (salt=504D114B, hash=1, iterations=10)
  

Le second enregistrement NSEC3 nous dit qu'il n'y a rien entre bbttmjm743srpq6j4kqdiuc73e3c1hoa et bshtf866a32e02rj617eue8ccp45a6v4. Donc, pas de biavqpkequ599fc57pv9d4sfg1h0mtkj, ce qui implique que com.gl n'existe pas. Réponse cohérente mais fausse (le domaine existe bien).

Notez que les deux autres serveurs faisant autorité pour .gl donnent une autre réponse, toute aussi fausse mais différente :


% dig +short NS gl
ns2.anycastdns.cz.
d.nic.gl.
ns1.anycastdns.cz.

% dig +dnssec @ns1.anycastdns.cz DS com.gl 
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 36229
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 1
...
;; AUTHORITY SECTION:
s2uojg57gtbj0m12ecau9csfd38ejndn.gl. 3600 IN NSEC3 1 1 10 504D114B SAGKR73F41OMFFI8TDE1CGHOQM502SIH NS SOA RRSIG DNSKEY NSEC3PARAM

  

Cette fois, on a un NOERROR, moins grave que le faux NXDOMAIN, mais le NSEC3 est pour gl, pas pour le nom demandé (d'où le message d'erreur de DNSviz).

Comment cela a pu se produire ? Je ne connais pas de logiciel ayant ce comportement, mais je note que le domaine a pris un sérieux risque : com.gl est délégué (il y a un ensemble d'enregistrements NS) mais aux mêmes serveurs que .gl. C'est une mauvaise pratique car elle rend plus difficile le déboguage (les données de la zone fille peuvent masquer celles de la zone parente). Ce n'est pas forcément la cause primaire, mais elle n'aide pas à déblayer le problème.

(Merci à Viktor Dukhovni, inlassable détecteur et débogueur de problèmes DNSSEC dans les TLD. Il vient d'ailleurs d'en trouver un autre pour le .scot.)


L'article seul

Articles des différentes années : 2023  2022  2021  2020  2019  2018  2017  Précédentes années

Syndication : Flux Atom avec seulement les résumés et Flux Atom avec tout le contenu.

Un article de ce blog au hasard.