11 points par GN⁺ 2026-01-01 | 5 commentaires | Partager sur WhatsApp
  • Après avoir déjà supprimé strncpy(), le projet cURL interdit désormais complètement strcpy() 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
  • 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 pour strncpy()

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;
    }
    
  • DEBUGASSERT permet 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 strcpy dans 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

 
ahwjdekf 2026-01-02

Il vaut mieux utiliser snprintf à la place de strcpy. S'il y a un strcpy dans le code, il faut retrouver l'adresse du développeur qui l'a écrit.

 
winmain 2026-01-02

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.

 
secwind 2026-01-02

Erreur C4996 strcpy : cette fonction ou variable peut ne pas être sûre. Envisagez d’utiliser strcpy_s à la place. Pour désactiver l’obsolescence, utilisez _CRT_SECURE_NO_WARNINGS. Voir l’aide en ligne pour plus de détails.

 
GN⁺ 2026-01-01
Réactions sur Hacker News
  • strcpy() est mauvais non seulement sur le plan de la sécurité, mais aussi des performances
    Autrefois, 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 inefficace

    • Je pense qu’il faut désormais abandonner autant que possible les chaînes terminées par un caractère nul elles-mêmes
    • Je n’ai pas vu récemment strcpy utiliser une boucle scalaire. Je me demande si ce n’est le cas que sur l’architecture ARM
  • J’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

    • strncpy a été créé pour copier des noms de fichiers à longueur fixe. Voir cette réponse StackOverflow pour plus de détails
    • Le fait de ne pas inclure la longueur dans la chaîne venait autrefois d’un souci d’économie de mémoire. À l’époque, même un octet comptait
    • Si les fonctions de chaîne de C ont causé tant de problèmes, c’est parce que leurs concepteurs les ont ajoutées sans en anticiper suffisamment les conséquences. Le fait que les tableaux soient implicitement convertis en pointeurs dans les arguments de fonction est aussi une erreur de conception fondamentale
    • Ce book-keeping supplémentaire était autrefois coûteux, mais aujourd’hui c’est tout à fait supportable
    • strncpy était à l’origine une fonction faite pour manipuler des champs de chaînes à largeur fixe. Par exemple, pour remplir de NUL un champ comme char username[20]. Voir la page de manuel string_copying.7
  • Je trouve étrange que curlx_strcopy ne renvoie pas d’indication de réussite ou d’échec
    On peut inspecter dest[0], mais cela présente un fort risque d’erreur et ce n’est pas intuitif

    • La version précédente renvoyait une erreur, alors que maintenant elle échoue silencieusement et définit une chaîne vide. C’est étrange
    • Il semble sans doute que si DEBUGASSERT(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érable
    • Avec une telle conception, je pense qu’il y a de fortes chances de voir sortir une CVE plus tard
  • strncpy() n’était à l’origine pas destiné aux chaînes terminées par un caractère nul, mais aux champs à longueur fixe
    Le problème a commencé quand les analyseurs statiques ont recommandé d’utiliser strncpy à la place de strcpy. Les vraies alternatives étaient snprintf ou strlcpy

    • strlcpy est une fonction de la famille BSD, donc absente de POSIX. La recommandation officielle est stpecpy, mais en pratique elle est presque inexistante. Voir la documentation associée
    • Si strncpy remplit 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 C
  • Cette 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 memcpy

  • J’ai trouvé marquante la phrase de l’article selon laquelle « strcpy est un appât qui pousse l’IA à produire de faux rapports de vulnérabilité »

    • En pratique, l’IA ne se contente pas de signaler simplement strcpy comme un problème, elle produit des preuves complexes avec des erreurs de logique, et les mainteneurs se fatiguent à les vérifier
    • Les personnes qui soumettent ce type de faux rapports ignorent soit que l’IA peut se tromper, soit s’en moquent, puisqu’un signalement erroné n’a aucun coût pour elles
    • Au final, le problème vient des gens qui utilisent l’IA pour des usages inadaptés
  • Le 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 Result en Rust

    • Result ne 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 »
    • L’idéal est de faire la validation non pas près du code consommateur, mais le plus tôt possible à la frontière du système. Mais cela demande un système de types expressif
    • Dans ce genre de cas, on peut aussi distinguer les types comme avec String et CharSequence en Java
  • 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 void quand la copie est impossible
    Mais dans un tel cas, il vaudrait mieux traiter cela comme une erreur et ne pas toucher au tampon. Se reposer uniquement sur DEBUGASSERT n’est pas rassurant

  • Fé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é

    • Supprimer strcpy ne rend pas pour autant le code sûr en mémoire
    • La police des graphiques semble conçue pour l’impression. Elle est trop petite pour un blog
 
hiongun 2026-01-04

Passer 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.