Je souhaite savoir laquelle de ces deux options est la plus sûre à utiliser:
#define MAXLEN 255
char buff[MAXLEN + 1]
sprintf(buff, "%.*s", MAXLEN, name)
snprintf(buff, MAXLEN, "%s", name)
Je crois comprendre que les deux sont identiques. Veuillez suggérer.
Les deux expressions que vous avez données sont pas équivalent: sprintf
ne prend aucun argument spécifiant le nombre maximal d’octets à écrire; il faut simplement un tampon de destination, une chaîne de format et un tas d'arguments. Par conséquent, il peut écrire plus d'octets que votre tampon ne dispose de suffisamment d'espace pour écrire, ce faisant, du code arbitraire. Le %.*s
n'est pas une solution satisfaisante pour les raisons suivantes:
strlen
; c'est une mesure du nombre de caractères dans la chaîne, pas de sa longueur en mémoire (c'est-à-dire qu'il ne compte pas le terminateur nul).sprintf
en ce qui concerne les dépassements de mémoire tampon. Avec snprintf
, un maximum fixe et clair est défini, quelles que soient les modifications apportées à la chaîne de formatage ou aux types d'entrée.Pour l'exemple simple dans la question, il pourrait ne pas y avoir beaucoup de différence de sécurité entre les deux appels. Cependant, dans le cas général, snprintf()
est probablement plus sécurisé. Une fois que vous avez une chaîne de format plus complexe avec plusieurs spécifications de conversion, il peut être difficile (ou presque impossible) de vous assurer que la longueur de la mémoire tampon est comptabilisée avec précision pour toutes les conversions - en particulier, les conversions précédentes ne produisant pas nécessairement un nombre fixe des caractères de sortie.
Donc, je resterais avec snprintf()
.
Un autre petit avantage de snprintf()
(bien que n'étant pas lié à la sécurité) est qu'il vous indique la taille de la mémoire tampon dont vous avez besoin.
Une dernière remarque - vous devez spécifier la taille réelle de la mémoire tampon dans l'appel snprintf()
- elle gérera la comptabilisation du terminateur nul pour vous:
snprintf(buff, sizeof(buff), "%s", name);
Je dirais que snprintf()
est beaucoup mieux tant que je n'ai pas lu ce passage:
https://buildsecurityin.us-cert.gov/bsi/articles/knowledge/coding/838-BSI.html
En résumé: snprintf()
non portable, changement de comportement d’un système à l’autre. Le problème le plus grave avec snprintf()
peut survenir lorsque snprintf()
est implémenté simplement en appelant sprintf()
.Vous pensez peut-être qu'il vous protège du dépassement de mémoire tampon et vous laisse baisser la garde, mais ce n'est pas le cas.
Alors maintenant, je dis toujours snprintf()
plus sûr, mais aussi prudent lorsque je l'utilise.
Votre déclaration de sprintf est correcte, mais je ne serais pas assez confiant pour l'utiliser pour des raisons de sécurité (par exemple, il manque un caractère cryptique et vous êtes sans bouclier) tant qu'il y a un snprintf qui peut être appliqué à n'importe quel format ... wait snprintf n’est pas en ANSI C . C'est (seulement?) C99. Cela pourrait être une (faible) raison de préférer l’autre.
Bien. Vous pouvez aussi utiliser strncpy
, n'est-ce pas?
par exemple.
char buffer[MAX_LENGTH+1];
buffer[MAX_LENGTH]=0; // just be safe in case name is too long
strncpy(buffer,MAX_LENGTH,name); // strncpy will never overwrite last byte
La meilleure et la plus souple serait d’utiliser snprintf
!
size_t nbytes = snprintf(NULL, 0, "%s", name) + 1; /* +1 for the '\0' */
char *str = malloc(nbytes);
snprintf(str, nbytes, "%s", name);
Dans C99, snprintf
renvoie le nombre d'octets écrits dans la chaîne, à l'exception de '\0'
. S'il y a moins d'octets que nécessaire, snprintf
renvoie le nombre d'octets qui aurait été nécessaire pour développer le format (en excluant toujours le '\0'
). En transmettant à snprintf
une chaîne de longueur 0, vous pouvez savoir à l'avance combien de temps aurait été la chaîne développée et l'utiliser pour allouer la mémoire nécessaire.
Il existe une différence importante entre ces deux méthodes: l'appel snprintf
analysera l'argument name
jusqu'à la fin (fin NUL) afin de déterminer la valeur de retour correcte. L’appel sprintf
, par contre, lira AT PLUS 255 caractères de name
.
Ainsi, si name
est un pointeur sur un tampon non terminé par NUL comportant au moins 255 caractères, l'appel snprintf
peut s'exécuter à la fin du tampon et déclencher un comportement non défini (tel qu'un blocage), alors que la version sprintf
ne le sera pas.
Les deux donneront le résultat souhaité, mais snprintf
est plus générique et protégera votre chaîne des dépassements, quelle que soit la chaîne de format indiquée.
De plus, étant donné que snprintf
(ou sprintf
ajoute) un \0
final, vous devez augmenter la taille du tampon de chaîne d'un octet, char buff[MAXLEN + 1]
.