Maison  >  Article  >  développement back-end  >  TIL CAnnexe K existe mais vous ne devriez pas l'utiliser

TIL CAnnexe K existe mais vous ne devriez pas l'utiliser

DDD
DDDoriginal
2024-11-03 13:58:03337parcourir

TIL CAnnex K exists but you shouldn

L'annexe K est le nom technique. Les autres mots-clés courants sont __STDC_LIB_EXT1__ et __STDC_WANT_LIB_EXT1__. L'annexe K définit les éléments de suffixe _s "sécurisés" comme sprintf_s() et scanf_s().

Consultez également l'expérience sur le terrain avec l'annexe K (2015) et la vérification des limites - documentation technique cppreference.com.

Le but

A quoi servent les fonctions _s() ? Ils vérifient leurs arguments pour plus d'invariants comme "appellera le gestionnaire de contraintes si le flux est nul, la chaîne est nulle, le bufsz est nul ou le tampon écrirait hors limites au-delà de la longueur spécifiée". Cela semble être une bonne idée, non ? Ouais! C’est le cas !

L'essentiel est que vous pouvez/pourriez faire ceci :

#define __STDC_WANT_LIB_EXT1__ 1
#include ade979de5fc0e1ca0540f360a64c230b

int main() {
  printf_s("Hello %s!\n", "Alan Turing");
  return 0;
}

Comment cela se compare-t-il à la façon normale de faire les choses sans __STDC_WANT_LIB_EXT1__ ?

Chemin heureux

FILE *file = fopen("hello.txt", "r");
// file is OK.
FILE *file;
errno_t err = fopen_s(&file, "hello.txt", "r");
// file is OK

Triste chemin

FILE *file = fopen("notexist.txt", "r");
// file is NULL, errno is set.
FILE *file;
errno_t err = fopen_s(&file, "notexist.txt", "r");
// file is NULL, err is set.

Mauvais chemin

FILE *file = fopen(NULL, NULL);
// idk.
FILE *file;
errno_t err = fopen_s(&file, NULL, NULL);
// Constraint violated. Abort with message.

Oui, vous pouvez personnaliser le gestionnaire de contraintes pour simplement vous connecter à un fichier et continuer comme si de rien n'était.

set_constraint_handler_s(ignore_handler_s);
set_constraint_handler_s(abort_handler_s);
set_constraint_handler_s(my_awesome_handler);

Remarquez comment le fopen() normal a la même valeur de retour (éventuellement un numéro d'erreur différent) pour indiquer différents niveaux de gravité des erreurs ? C'est un peu ce que fopen_s() essayait d'améliorer. Du moins, c'est ma lecture. J'y pense comme à la panique de Rust !() par rapport à un Resulte1c7801d19b137084b3a257467b07ace renvoyé. Cela aide probablement aussi à arrêter certaines attaques par débordement de tampon en fournissant des arguments size_of_dest pour éviter de déborder des tampons de destination comme strcpy_s() et gets_s().

char* gets( char* str ); // (removed in C11)
char* gets_s( char* str, rsize_t n ); // (since C11, annex K)

Lit stdin dans le tableau de caractères pointé par str jusqu'à ce qu'un caractère de nouvelle ligne soit trouvé ou que la fin du fichier se produise. Un caractère nul est écrit immédiatement après le dernier caractère lu dans le tableau. Le caractère de nouvelle ligne est supprimé mais n'est pas stocké dans le tampon.

La fonction gets() n'effectue pas de vérification des limites, cette fonction est donc extrêmement vulnérable aux attaques par débordement de tampon. Il ne peut pas être utilisé en toute sécurité (sauf si le programme s'exécute dans un environnement qui restreint ce qui peut apparaître sur stdin). Pour cette raison, la fonction a été obsolète dans le troisième rectificatif à la norme C99 et complètement supprimée dans la norme C11. fgets() et gets_s() sont les remplacements recommandés.

ATTENTION : n'utilisez jamais gets().

// BAD
char buffer[1000];
gets(buffer);
// ⚠️ Could write >1000 chars to `buffer`!
// GOOD
char buffer[1000];
gets_s(buffer, sizeof(buffer));
// This will stop at 1000 chars.

La fonction _s() semble plutôt sympa pour arrêter les endroits courants où des débordements de tampon peuvent se produire.

Le problème

Ils ne sont pas implémentés partout. Les fonctions _s() sont une extension qui n'est pas disponible dans les implémentations libc comme la glibc de GNU. Il y a d'autres problèmes mineurs comme le fait qu'il n'est pas ergonomique pour le multithreading et l'erreur courante de faire sizeof(src) au lieu de sizeof(dest) pour des choses comme strcpy_s(), mais tout cela n'est rien en comparaison du problème de disponibilité.

La plupart des informations en ligne que je peux trouver semblent indiquer que MSVC est le seul compilateur/libc majeur à avoir implémenté l'annexe K.

Étant donné que ces fonctions sophistiquées _s() ne sont pas partout où votre code doit être compilé, vous devrez écrire du code comme celui-ci :

#define __STDC_WANT_LIB_EXT1__ 1
#include ade979de5fc0e1ca0540f360a64c230b

int main() {
  printf_s("Hello %s!\n", "Alan Turing");
  return 0;
}

...pour chaque instance que vous souhaitez exécuter strlen_s() ou fopen_s() ou strcpy_s(). C'est une bonne façon de devenir fou.

Donc, évidemment, vous n'allez pas écrire de code dépendant de la plate-forme juste pour faire des printf() et strcpy() de base mais qu'en est-il d'envelopper tout cela #ifdef __STDC_LIB_EXT1__ #else dans une bibliothèque ?

J'ai trouvé deux bibliothèques prometteuses via une recherche rapide sur Google :

  • safec : page GitHub du site Web de la bibliothèque Safe C ⭐335
  • sbaresearch/slibc : Implémentation de l'Annexe K C11 "Interfaces de vérification des limites" ISO/IEC 9899:2011 ⭐14

Donc... si vous souhaitez (ou êtes obligé par des raisons de sécurité) d'utiliser les fonctions _s() mais que vous ne voulez pas non plus vous limiter à MSVC, vous pouvez utiliser l'une de ces ☝ bibliothèques.

? Pour en savoir plus, consultez Expérience sur le terrain avec l'annexe K (2015) et la vérification des limites - documentation technique cppreference.com.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn