Utilisez-vous les fonctions TR 24731 ‘safe’?

Le comité ISO C ( ISO / IEC JTC1 / SC21 / WG14 ) a publié le TR 24731-1 et travaille sur le TR 24731-2 :

TR 24731-1: Extensions de la bibliothèque C, partie I: interfaces de vérification des limites

WG14 travaille sur un TR sur des fonctions de bibliothèque C plus sûres. Ce TR est orienté vers la modification des programmes existants, souvent en ajoutant un paramètre supplémentaire à la longueur du tampon. La dernière version est dans le document N1225. Une justification est dans le document N1173. Cela doit devenir un rapport technique de type 2.

TR 24731-2: Extensions de la bibliothèque C – Partie II: Fonctions d’allocation dynamic

WG14 travaille sur un TR sur des fonctions de bibliothèque C plus sûres. Ce TR est orienté vers de nouveaux programmes utilisant l’allocation dynamic au lieu d’un paramètre supplémentaire pour la longueur du tampon. La dernière version est dans le document N1337. Cela doit devenir un rapport technique de type 2.

Des questions

  • Utilisez-vous une bibliothèque ou un compilateur prenant en charge les fonctions TR24731-1?
  • Si oui, quel compilateur ou quelle bibliothèque et sur quelle (s) plate-forme (s)?
  • Avez-vous découvert des bogues à la suite de la correction de votre code pour utiliser ces fonctions?
  • Quelles fonctions fournissent le plus de valeur?
  • Y en a-t-il qui ne fournissent aucune valeur ou valeur négative?
  • Envisagez-vous d’utiliser la bibliothèque à l’avenir?
  • Suivez-vous le travail du TR24731-2?

J’ai été un critique vocal de ces TR depuis leur création (quand il s’agissait d’un seul TR) et je ne les utiliserais jamais dans aucun de mes logiciels. Ils masquent les symptômes au lieu de s’attaquer aux causes et, à mon avis, ils auront un impact négatif sur la conception des logiciels car ils créent un faux sentiment de sécurité au lieu de promouvoir des pratiques existantes capables d’atteindre les mêmes objectives beaucoup plus efficacement. Je ne suis pas seul, en fait, je ne connais pas un seul grand promoteur en dehors du comité qui élabore ces rapports techniques.

J’utilise glibc et en tant que tel je sais que je vais être épargné d’avoir à faire face à ce non-sens, comme Ulrich Drepper, responsable principal de glibc, a déclaré à propos du sujet :

La bibliothèque safe (r) ISO C proposée ne parvient pas à résoudre complètement le problème. … Proposer de rendre la vie d’un programmeur encore plus difficile n’aidera pas. Mais c’est exactement ce qui est proposé. … Ils exigent tous plus de travail ou sont tout simplement stupides.

Il continue en détaillant les problèmes avec un certain nombre de fonctions proposées et a indiqué ailleurs que la glibc ne le soutiendrait jamais.

Le groupe Austin (responsable de la maintenance de POSIX) a procédé à un examen très critique du rapport technique, de ses commentaires et des réponses du comité disponibles ici . La revue du groupe Austin fait un très bon travail en détaillant de nombreux problèmes avec le TR, donc je ne vais pas entrer dans les détails individuels ici.

Donc, le résultat est le suivant: je n’utilise pas une implémentation qui supporte ou supportera cela, je ne prévois jamais utiliser ces fonctions, et je ne vois aucune valeur positive dans le TR. Personnellement, je pense que la seule raison pour laquelle le TR est toujours vivant, sous quelque forme que ce soit, est qu’il est fortement sollicité par Microsoft, qui s’est récemment montré capable de faire avancer les choses en dépit des nombreuses oppositions. Si ces fonctions sont normalisées, je ne pense pas qu’elles seront utilisées à grande échelle car la proposition existe depuis quelques années et n’a pas réussi à susciter un réel soutien de la part de la communauté.

Réponse directe à la question

J’aime la réponse de Robert, mais j’ai aussi quelques points de vue sur les questions que j’ai soulevées.

  • Utilisez-vous une bibliothèque ou un compilateur prenant en charge les fonctions TR24731-1?

    Non, pas moi

  • Si oui, quel compilateur ou quelle bibliothèque et sur quelle (s) plate-forme (s)?

    Je crois que les fonctions sont fournies par MS Visual Studio (édition MS VC ++ 2008, par exemple) et il existe des avertissements pour vous encourager à les utiliser.

  • Avez-vous découvert des bogues à la suite de la correction de votre code pour utiliser ces fonctions?

    Pas encore. Et je ne m’attends pas à en découvrir beaucoup dans mon code. Certains des autres codes avec lesquels je travaille – peut-être. Mais je n’ai pas encore été convaincu.

  • Quelles fonctions fournissent le plus de valeur?

    J’aime le fait que la famille de fonctions printf_s () n’accepte pas le spécificateur de format ‘ %n ‘.

  • Y en a-t-il qui ne fournissent aucune valeur ou valeur négative?

    Les fonctions tmpfile_s() et tmpnam_s() sont une déception horrible. Ils avaient vraiment besoin de travailler davantage comme mkstemp() qui à la fois crée le fichier et l’ouvre pour s’assurer qu’il n’y a pas de vulnérabilité TOCTOU (heure de vérification, heure d’utilisation). En l’état actuel, ces deux éléments apportent très peu de valeur.

    Je pense aussi que strerrorlen_s() fournit très peu de valeur.

  • Envisagez-vous d’utiliser la bibliothèque à l’avenir?

    Je suis dans deux esprits à ce sujet. J’ai commencé à travailler sur une bibliothèque qui implémenterait les capacités du TR 24731 sur une bibliothèque C standard, mais je me suis heurtée à la quantité de tests unitaires nécessaires pour démontrer son bon fonctionnement. Je ne suis pas sûr de continuer. J’ai du code que je veux porter sur Windows (principalement par désir pervers de fournir un support sur toutes les plates-formes – cela fonctionne depuis quelques décennies sur les dérivés Unix). Malheureusement, pour le comstackr sans avertissements des compilateurs MSVC, je dois plâtrer le code avec des éléments pour éviter que MSVC ne vienne me tuer en utilisant les fonctions de bibliothèque C standard parfaitement fiables (lorsqu’elles sont utilisées avec précaution). Et ce n’est pas appétissant. Il est déjà assez grave que je doive faire face à presque deux décennies d’un système qui s’est développé au cours de cette période; devoir faire face à l’idée d’amusement de quelqu’un (faire en sorte que les gens adoptent TR 24731 alors qu’ils n’en ont pas besoin) est ennuyant. C’est en partie la raison pour laquelle j’ai commencé le développement de la bibliothèque – pour me permettre d’utiliser les mêmes interfaces sous Unix et Windows. Mais je ne suis pas sûr de ce que je vais faire d’ici.

  • Suivez-vous le travail du TR24731-2?

    Je ne l’avais pas suivi jusqu’à ce que j’aille sur le site des normes tout en collectant les données pour la question. Les fonctions asprintf() et vasprintf() sont probablement utiles; Je les utiliserais Je ne suis pas certain des fonctions d’E / S du stream mémoire. Avoir strdup() normalisé au niveau C serait un grand pas en avant. Cela me semble moins controversé que les interfaces de la partie 1 (vérification des limites).

Dans l’ensemble, je ne suis pas convaincu par la partie 1 «Interfaces de vérification des limites». Le contenu du projet de la partie 2 «Fonctions d’allocation dynamic» est meilleur.

Si cela ne tenait qu’à moi, je me déplacerais un peu dans le sens de la partie 1, mais j’avais aussi révisé les interfaces de la bibliothèque C99 standard C qui renvoient un caractère char * au début de la chaîne (par exemple, strcpy() et strcat() ) au lieu de renvoyer un pointeur au début, ils renverraient un pointeur sur l’octet nul à la fin de la nouvelle chaîne. Cela rendrait certains idiomes communs (tels que la concaténation répétée de chaînes à la fin d’un autre) plus efficaces car cela éviterait le comportement quadratique du code qui utilise de manière répétée strcat() . Les remplacements assureraient tous la terminaison nulle des chaînes de sortie, comme le font les versions TR24731. Je ne suis pas totalement opposé à l’idée de l’interface de vérification, ni aux fonctions de gestion des exceptions. C’est une affaire délicate.


L’implémentation de Microsoft n’est pas la même que la spécification standard

Mise à jour (2011-05-08)

Voir aussi cette question Malheureusement, et fatalement à l’utilité des fonctions du TR24731, les définitions de certaines fonctions diffèrent entre l’implémentation Microsoft et la norme, ce qui les rend inutiles (pour moi). Ma réponse cite vsnprintf_s() .

Par exemple, TR 24731-1 indique que l’interface avec vsnprintf_s() est:

 #define __STDC_WANT_LIB_EXT1__ 1 #include  #include  int vsnprintf_s(char * ressortingct s, rsize_t n, const char * ressortingct format, va_list arg); 

Malheureusement, MSDN dit que l’interface avec vsnprintf_s() est:

 int vsnprintf_s( char *buffer, size_t sizeOfBuffer, size_t count, const char *format, va_list argptr ); 

Paramètres

  • buffer – Emplacement de stockage pour la sortie.
  • sizeOfBuffer – La taille du tampon pour la sortie.
  • count – Nombre maximum de caractères à écrire (sans la valeur NULL finale) ou _TRUNCATE.
  • format – Spécification du format.
  • argptr – Pointeur vers la liste des arguments.

Notez qu’il ne s’agit pas simplement d’un mappage de type: le nombre d’arguments fixes est différent et donc inconciliable. Il n’est pas clair non plus pour moi (et probablement aussi pour le comité de normalisation) quels sont les avantages d’avoir à la fois «sizeOfBuffer» et «count»; il ressemble deux fois aux mêmes informations (ou, au moins, le code sera généralement écrit avec la même valeur pour les deux parameters).

De même, il y a aussi des problèmes avec scanf_s() et ses proches. Microsoft indique que le type du paramètre de longueur de la mémoire tampon est unsigned (indiquant explicitement que «le paramètre size est de type unsigned , not size_t »). En revanche, dans l’Annexe K, le paramètre size est de type rsize_t , qui est la variante restreinte de size_t ( rsize_t est un autre nom pour size_t , mais RSIZE_MAX est plus petit que SIZE_MAX ). Donc, encore une fois, le code appelant scanf_s() devrait être écrit différemment pour Microsoft C et Standard C.

À l’origine, je prévoyais d’utiliser les fonctions «sûres» comme moyen de comstackr correctement du code sous Windows et Unix, sans avoir à écrire de code conditionnel. Comme cela est vaincu parce que les fonctions Microsoft et ISO ne sont pas toujours les mêmes, il est temps d’abandonner.


Modifications dans le vsnprintf() de Microsoft dans Visual Studio 2015

Dans la documentation de Visual Studio 2015 pour vsnprintf() , il note que l’interface a changé:

À compter de l’UCRT dans Visual Studio 2015 et Windows 10, vsnprintf n’est plus identique à _vsnprintf . La fonction vsnprintf est conforme à la norme C99; _vnsprintf est conservé pour compatibilité avec les _vnsprintf .

Cependant, l’interface Microsoft pour vsnprintf_s() n’a pas changé.


Autres exemples de différences entre Microsoft et l’Annexe K

La variante standard C11 de localtime_s() est définie dans l’annexe K.3.8.2.4 de l’ISO / IEC 9899:

 struct tm *localtime_s(const time_t * ressortingct timer, struct tm * ressortingct result); 

par rapport à la variante MSDN de localtime_s() définie comme:

 errno_t localtime_s(struct tm* _tm, const time_t *time); 

et la variante POSIX localtime_r() définie comme suit:

 struct tm *localtime_r(const time_t *ressortingct timer, struct tm *ressortingct result); 

La norme C11 et les fonctions POSIX sont équivalentes en dehors du nom. La fonction Microsoft est différente dans l’interface même si elle partage un nom avec le standard C11.

Un autre exemple de différences est strtok_s() et strtok_s() de strtok_s() :

 char *strtok_s(char *strToken, const char *strDelimit, char **context); 

contre:

 char *strtok_s(char * ressortingct s1, rsize_t * ressortingct s1max, const char * ressortingct s2, char ** ressortingct ptr); 

Notez que la variante de Microsoft a 3 arguments alors que la variante de l’Annexe K en a 4. Cela signifie que la liste d’arguments de Microsoft strtok_s() est compatible avec strtok_r() de POSIX – les appels sont donc interchangeables si par une macro) – mais la version Standard C (Annexe K) est différente des deux avec l’argument supplémentaire.

La question Différentes déclarations de qsort_r() sur Mac et Linux ont une réponse qui traite également de qsort_s() tel que défini par Microsoft et qsort_s() tel que défini par TR24731-1 – là encore, les interfaces sont différentes.


ISO / IEC 9899: 2011 – Norme C11

La norme C11 (version préliminaire de décembre 2010 ; vous pouvez obtenir une copie PDF de la norme définitive ISO / IEC 9899: 2011 du magasin en ligne de l’ANSI pour 30 USD) contient les fonctions du TR24731-1 en tant que partie facultative de la norme. la norme. Ils sont définis dans l’Annexe K (Interfaces de vérification des limites), qui est «normative» plutôt que «informative», mais facultative.

Le standard C11 ne possède pas les fonctions du TR24731-2 – ce qui est sortingste car la fonction vasprintf() et ses proches peuvent être vraiment utiles.

Résumé rapide:

  • C11 contient TR24731-1
  • C11 ne contient pas TR24731-2

Proposition de suppression de l’annexe K du successeur à C11

Déduplicateur a souligné dans un commentaire sur une autre question qu’il existe une proposition devant le comité de normalisation ISO C (ISO / IEC JTC1 / SC22 / WG14)

  • N1967 Expérience sur le terrain avec l’Annexe K – Interfaces de vérification des limites

Il contient des références à certaines des implémentations existantes des fonctions de l’Annexe K – aucune d’entre elles n’est largement utilisée (mais vous pouvez les trouver via le document si cela vous intéresse).

Le document se termine par la recommandation:

Par conséquent, nous proposons que l’annexe K soit retirée de la prochaine révision de la norme C, soit abandonnée puis supprimée.

Je soutiens cette recommandation.

Ok, maintenant un stand pour TR24731-2:

Oui, j’ai utilisé asprintf() / vasprintf() depuis que je les ai vus dans la glibc, et oui, j’en suis un très grand partisan.

Pourquoi? Parce qu’ils fournissent précisément ce dont j’ai besoin encore et encore: un moyen puissant, flexible, sûr et (relativement) facile à utiliser pour formater un texte dans une chaîne fraîchement allouée.

Je suis aussi très en faveur des memstreams: comme asprintf() , open_memstream() (pas fmemopen() !!!) alloue un tampon suffisamment grand pour vous et vous donne un FILE* pour faire vos impressions, donc vos fonctions d’impression peuvent ignorez complètement s’ils impriment dans une chaîne ou un fichier et vous pouvez simplement oublier la question, combien d’espace vous aurez besoin.

Utilisez-vous une bibliothèque ou un compilateur prenant en charge les fonctions TR24731-1? Si oui, quel compilateur ou quelle bibliothèque et sur quelle (s) plate-forme (s)?

Oui, Visual Studio 2005 & 2008 (pour le développement Win32 évidemment).

Avez-vous découvert des bogues à la suite de la correction de votre code pour utiliser ces fonctions?

Sorte de …. J’ai écrit ma propre bibliothèque de fonctions sûres (seulement environ 15 que nous utilisons fréquemment) qui seraient utilisées sur plusieurs plates-formes – Linux, Windows, VxWorks, INtime, RTX et uItron. La raison de la création des fonctions sécurisées était la suivante:

  • Nous avons rencontré un grand nombre de bogues en raison d’une mauvaise utilisation des fonctions C standard.
  • Je n’étais pas satisfait des informations transmises ou renvoyées par les fonctions TR ou, dans certains cas, de leurs alternatives POSIX.

Une fois les fonctions écrites, d’autres bogues ont été découverts. Donc oui, il y avait de la valeur à utiliser les fonctions.

Quelles fonctions fournissent le plus de valeur?

Versions plus sûres de vsnprintf, strncpy, strncat.

Y en a-t-il qui ne fournissent aucune valeur ou valeur négative?

fopen_s et fonctions similaires ajoutent très peu de valeur pour moi personnellement. Je vais bien si fopen renvoie NULL. Vous devez toujours vérifier la valeur de retour de la fonction. Si quelqu’un ignore la valeur de retour de fopen, qu’est-ce qui lui fera vérifier la valeur de retour de fopen_s? Je comprends que fopen_s retournera des informations d’erreur plus spécifiques qui peuvent être utiles dans certains contextes. Mais pour ce que je travaille, cela n’a pas d’importance.

Envisagez-vous d’utiliser la bibliothèque à l’avenir?

Nous l’utilisons maintenant – dans notre propre bibliothèque “sûre”.

Suivez-vous le travail du TR24731-2?

Non.

Non, ces fonctions sont absolument inutiles et ne servent à rien d’autre que d’encourager l’écriture de code pour qu’elles ne soient compilées que sous Windows.

snprintf est parfaitement sûr (quand il est implémenté correctement) donc snprintf_s est inutile. strcat_s détruira les données si le tampon est saturé (en effaçant la chaîne concaténée). Il y a beaucoup d’autres exemples d’ignorance complète de la façon dont les choses fonctionnent.

Les véritables fonctions utiles sont le strlcpy et le strlcat de BSD. Mais Microsoft et Drepper les ont tous deux rejetés pour des raisons égoïstes, au grand dam des programmeurs du monde entier.