`strcpy` interdit lui aussi
(daniel.haxx.se)- Après avoir déjà supprimé
strncpy(), le projet cURL interdit désormais complètementstrcpy()dans sa base de code - L’API de
strcpy()est simple, mais elle présente un risque de dissociation entre la copie et la vérification de la taille du tampon, ce qui la rend peu sûre sur le long terme en maintenance - Pour la remplacer, une nouvelle fonction nommée
curlx_strcopy()a été introduite ; elle prend en argument à la fois la taille du tampon de destination et la longueur de la chaîne afin de vérifier si la copie est possible avant de l’exécuter - Cette fonction utilise
memcpy()en interne et garantit aussi le traitement du caractère de fin nul - Ce changement permet de renforcer la sécurité et la cohérence du code, tout en réduisant aussi les faux signalements de vulnérabilités générés par l’IA
Contexte de la suppression de strcpy
- cURL avait déjà supprimé tous les appels à
strncpy()par le passé, en pointant les problèmes de son API peu intuitive, de l’absence de garantie de terminaison nulle et du remplissage inutile par des zéros- Quand une copie partielle de chaîne est nécessaire, le projet est passé à
memcpy()avec gestion explicite de la terminaison nulle
- Quand une copie partielle de chaîne est nécessaire, le projet est passé à
- L’API de
strcpy()est simple, mais comme elle n’indique pas explicitement la taille du tampon, il existe un risque qu’en maintenance le code de validation soit séparé de l’appel de copie- Si le code est modifié pendant des décennies par plusieurs développeurs, la vérification de la taille du tampon peut devenir inopérante
Introduction d’une nouvelle fonction de copie de chaînes
- Pour éviter ce risque, une fonction de remplacement appelée
curlx_strcopy()a été introduite- Elle prend comme arguments le tampon de destination, la taille du tampon, le tampon source et la longueur de la chaîne source
- Elle n’effectue la copie avec
memcpy()que si la copie et la terminaison nulle sont toutes deux possibles - En cas d’échec, le tampon de destination est initialisé comme chaîne vide
- Cette fonction demande davantage d’arguments et plus de code que
strcpy(), mais elle lie étroitement la vérification du tampon à l’opération de copie afin d’assurer la sécurité - L’usage de
strcpy()est désormais totalement interdit dans la base de code de cURL, comme cela a déjà été fait pourstrncpy()
Détails d’implémentation
- Voici un exemple de définition de la fonction
void curlx_strcopy(char *dest, size_t dsize, const char *src, size_t slen) { DEBUGASSERT(slen < dsize); if(slen < dsize) { memcpy(dest, src, slen); dest[slen] = 0; } else if(dsize) dest[0] = 0; } DEBUGASSERTpermet de détecter plus tôt les erreurs pendant le développement, et la fonction est conçue pour toujours réussir en environnement de production- Comme
strcpy, elle n’a pas de valeur de retour, et le projet privilégie la détection des erreurs lors des tests et du fuzzing
Réactions de la communauté
- Certains développeurs ont fait remarquer une ressemblance avec
strcpy_s()(C11 Annex K), mais cURL continue d’utiliser la norme C89 - D’autres ont suggéré d’ajouter une valeur de retour ou d’améliorer le traitement en cas d’échec du tampon
- En réponse, l’équipe de cURL a expliqué que « la fonction a été conçue pour toujours réussir, donc une valeur de retour est inutile »
Effet secondaire lié à l’IA
- Ce changement permet aussi d’éviter que des chatbots d’IA détectent à tort l’usage de
strcpydans le code de cURL et affirment qu’il est “vulnérable” - L’auteur note toutefois que « l’IA peut encore produire d’autres faux signalements », évoquant ainsi les limites de l’analyse de code fondée sur l’IA
5 commentaires
Il vaut mieux utiliser
snprintfà la place destrcpy. S'il y a unstrcpydans le code, il faut retrouver l'adresse du développeur qui l'a écrit.C’était la méthode que j’utilisais avec du code de debug quand je travaillais dans une société de jeux il y a 25 ans, et ce n’était évidemment pas seulement
strcpy. En release, le service était ensuite déployé en les desserrant à nouveau pour gagner en vitesse. En fait, dans le jeu vidéo, on est particulièrement sensible aux collisions mémoire, donc on travaillait aussi avec une vigilance extrême sur ce point, au point de créer et d’utiliser notre propre débogueur mémoire. Et aujourd’hui, quand j’y repense, je me rends compte qu’on était en train de fabriquer un ramasse-miettes. Que de souvenirs.Erreur C4996
strcpy: cette fonction ou variable peut ne pas être sûre. Envisagez d’utiliserstrcpy_sà la place. Pour désactiver l’obsolescence, utilisez_CRT_SECURE_NO_WARNINGS. Voir l’aide en ligne pour plus de détails.Réactions sur Hacker News
strcpy()est mauvais non seulement sur le plan de la sécurité, mais aussi des performancesAutrefois, on pensait que
strcpy()était efficace quand on ne connaissait pas la longueur d’une chaîne, mais en réalité sa structure copie un octet à la fois, ce qui oblige le CPU à faire de la prédiction de branchement, et c’est inefficacestrcpyutiliser une boucle scalaire. Je me demande si ce n’est le cas que sur l’architecture ARMJ’ai toujours trouvé que les routines de chaînes de C avaient toutes de grosses limitations, au point d’être peu utiles
Je pense donc qu’il faut absolument une bibliothèque qui enregistre la taille de la mémoire allouée avec le pointeur de chaîne
On peut par exemple regarder la bibliothèque bstring
strncpya été créé pour copier des noms de fichiers à longueur fixe. Voir cette réponse StackOverflow pour plus de détailsstrncpyétait à l’origine une fonction faite pour manipuler des champs de chaînes à largeur fixe. Par exemple, pour remplir de NUL un champ commechar username[20]. Voir la page de manuel string_copying.7Je trouve étrange que
curlx_strcopyne renvoie pas d’indication de réussite ou d’échecOn peut inspecter
dest[0], mais cela présente un fort risque d’erreur et ce n’est pas intuitifDEBUGASSERT(slen < dsize);passe, cela soit considéré comme un succès, mais dans un build release, l’assert peut être supprimé. Un code d’erreur explicite serait préférablestrncpy()n’était à l’origine pas destiné aux chaînes terminées par un caractère nul, mais aux champs à longueur fixeLe problème a commencé quand les analyseurs statiques ont recommandé d’utiliser
strncpyà la place destrcpy. Les vraies alternatives étaientsnprintfoustrlcpystrlcpyest une fonction de la famille BSD, donc absente de POSIX. La recommandation officielle eststpecpy, mais en pratique elle est presque inexistante. Voir la documentation associéestrncpyremplit après le caractère nul, c’était pour permettre des comparaisons efficaces dans des champs de noms à longueur fixe comme les entrées de répertoire. C’est aussi indiqué dans les documents de justification de la norme ANSI CCette API fait penser à l’Annexe K. L’espace pour NUL est inclus dans la taille du tampon de destination, mais pas dans la taille de la source
Je pense qu’il vaut mieux utiliser directement
memcpyJ’ai trouvé marquante la phrase de l’article selon laquelle «
strcpyest un appât qui pousse l’IA à produire de faux rapports de vulnérabilité »strcpycomme un problème, elle produit des preuves complexes avec des erreurs de logique, et les mainteneurs se fatiguent à les vérifierLe principe « vérifier au plus près du code » est bon, mais cela devient ambigu quand il faut valider tôt dans le cycle de vie des données
Ce serait bien de pouvoir distinguer par le type qu’il s’agit de données validées, comme avec le type
Resulten RustResultne contient qu’un succès ou un échec, et ne garantit pas l’état validé. Il vaut mieux avoir un type distinct qui ne peut être créé qu’après validation. C’est la philosophie « parse, don’t validate »La différence de type off-by-one entre la taille du tampon et la longueur de la chaîne est un horrible problème d’ergonomie. Elle continuera probablement à provoquer des erreurs
La nouvelle fonction proposée de copie de chaîne vide le tampon de destination et renvoie
voidquand la copie est impossibleMais dans un tel cas, il vaudrait mieux traiter cela comme une erreur et ne pas toucher au tampon. Se reposer uniquement sur
DEBUGASSERTn’est pas rassurantFélicitations pour l’aboutissement du projet. Avec suffisamment d’efforts, C/C++ peuvent aussi atteindre une certaine sécurité mémoire
En revanche, sur mobile, la taille de police des graphiques est trop petite et nuit à la lisibilité
strcpyne rend pas pour autant le code sûr en mémoirePasser carrément au langage C3 est aussi une bonne idée. Comme il reprend la syntaxe du C avec un minimum de changements tout en ajoutant des fonctionnalités modernes, la migration est aussi facile.